fal 1.13.4__tar.gz → 1.14.0__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 fal might be problematic. Click here for more details.

Files changed (176) hide show
  1. {fal-1.13.4/fal.egg-info → fal-1.14.0}/PKG-INFO +2 -2
  2. {fal-1.13.4 → fal-1.14.0/fal.egg-info}/PKG-INFO +2 -2
  3. {fal-1.13.4 → fal-1.14.0}/fal.egg-info/SOURCES.txt +1 -0
  4. {fal-1.13.4 → fal-1.14.0}/fal.egg-info/requires.txt +1 -1
  5. {fal-1.13.4 → fal-1.14.0}/pyproject.toml +1 -1
  6. {fal-1.13.4 → fal-1.14.0}/src/fal/_fal_version.py +2 -2
  7. {fal-1.13.4 → fal-1.14.0}/src/fal/api.py +5 -0
  8. {fal-1.13.4 → fal-1.14.0}/src/fal/auth/__init__.py +20 -0
  9. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/_utils.py +1 -1
  10. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/apps.py +6 -1
  11. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/auth.py +30 -24
  12. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/profile.py +7 -4
  13. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/teams.py +2 -9
  14. {fal-1.13.4 → fal-1.14.0}/src/fal/config.py +17 -4
  15. {fal-1.13.4 → fal-1.14.0}/src/fal/container.py +3 -4
  16. fal-1.14.0/src/fal/project.py +81 -0
  17. {fal-1.13.4 → fal-1.14.0}/src/fal/sdk.py +16 -4
  18. {fal-1.13.4 → fal-1.14.0}/tests/test_apps.py +88 -139
  19. {fal-1.13.4 → fal-1.14.0}/.gitignore +0 -0
  20. {fal-1.13.4 → fal-1.14.0}/Makefile +0 -0
  21. {fal-1.13.4 → fal-1.14.0}/README.md +0 -0
  22. {fal-1.13.4 → fal-1.14.0}/docs/conf.py +0 -0
  23. {fal-1.13.4 → fal-1.14.0}/docs/index.rst +0 -0
  24. {fal-1.13.4 → fal-1.14.0}/fal.egg-info/dependency_links.txt +0 -0
  25. {fal-1.13.4 → fal-1.14.0}/fal.egg-info/entry_points.txt +0 -0
  26. {fal-1.13.4 → fal-1.14.0}/fal.egg-info/top_level.txt +0 -0
  27. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/README.md +0 -0
  28. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  29. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  30. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  31. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  32. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  33. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  34. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  35. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  36. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  37. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  38. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  39. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  40. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  41. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  42. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  43. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  44. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  45. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  46. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  47. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  48. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  49. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  50. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  51. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  52. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  53. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  54. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  55. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  56. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  57. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  58. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  59. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  60. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  61. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  62. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  63. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  64. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  65. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  66. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  67. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  68. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  69. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  70. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  71. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  72. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  73. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  74. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  75. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  76. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  77. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  78. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  79. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  80. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  81. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  82. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  83. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  84. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  85. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  86. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  87. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  88. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  89. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  90. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  91. {fal-1.13.4 → fal-1.14.0}/openapi-fal-rest/pyproject.toml +0 -0
  92. {fal-1.13.4 → fal-1.14.0}/openapi_rest.config.yaml +0 -0
  93. {fal-1.13.4 → fal-1.14.0}/setup.cfg +0 -0
  94. {fal-1.13.4 → fal-1.14.0}/src/fal/__init__.py +0 -0
  95. {fal-1.13.4 → fal-1.14.0}/src/fal/__main__.py +0 -0
  96. {fal-1.13.4 → fal-1.14.0}/src/fal/_serialization.py +0 -0
  97. {fal-1.13.4 → fal-1.14.0}/src/fal/_version.py +0 -0
  98. {fal-1.13.4 → fal-1.14.0}/src/fal/app.py +0 -0
  99. {fal-1.13.4 → fal-1.14.0}/src/fal/apps.py +0 -0
  100. {fal-1.13.4 → fal-1.14.0}/src/fal/auth/auth0.py +0 -0
  101. {fal-1.13.4 → fal-1.14.0}/src/fal/auth/local.py +0 -0
  102. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/__init__.py +0 -0
  103. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/api.py +0 -0
  104. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/cli_nested_json.py +0 -0
  105. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/create.py +0 -0
  106. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/debug.py +0 -0
  107. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/deploy.py +0 -0
  108. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/doctor.py +0 -0
  109. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/keys.py +0 -0
  110. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/main.py +0 -0
  111. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/parser.py +0 -0
  112. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/run.py +0 -0
  113. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/runners.py +0 -0
  114. {fal-1.13.4 → fal-1.14.0}/src/fal/cli/secrets.py +0 -0
  115. {fal-1.13.4 → fal-1.14.0}/src/fal/console/__init__.py +0 -0
  116. {fal-1.13.4 → fal-1.14.0}/src/fal/console/icons.py +0 -0
  117. {fal-1.13.4 → fal-1.14.0}/src/fal/console/ux.py +0 -0
  118. {fal-1.13.4 → fal-1.14.0}/src/fal/exceptions/__init__.py +0 -0
  119. {fal-1.13.4 → fal-1.14.0}/src/fal/exceptions/_base.py +0 -0
  120. {fal-1.13.4 → fal-1.14.0}/src/fal/exceptions/_cuda.py +0 -0
  121. {fal-1.13.4 → fal-1.14.0}/src/fal/exceptions/auth.py +0 -0
  122. {fal-1.13.4 → fal-1.14.0}/src/fal/files.py +0 -0
  123. {fal-1.13.4 → fal-1.14.0}/src/fal/flags.py +0 -0
  124. {fal-1.13.4 → fal-1.14.0}/src/fal/logging/__init__.py +0 -0
  125. {fal-1.13.4 → fal-1.14.0}/src/fal/logging/isolate.py +0 -0
  126. {fal-1.13.4 → fal-1.14.0}/src/fal/logging/style.py +0 -0
  127. {fal-1.13.4 → fal-1.14.0}/src/fal/logging/trace.py +0 -0
  128. {fal-1.13.4 → fal-1.14.0}/src/fal/logging/user.py +0 -0
  129. {fal-1.13.4 → fal-1.14.0}/src/fal/py.typed +0 -0
  130. {fal-1.13.4 → fal-1.14.0}/src/fal/rest_client.py +0 -0
  131. {fal-1.13.4 → fal-1.14.0}/src/fal/sync.py +0 -0
  132. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/__init__.py +0 -0
  133. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/exceptions.py +0 -0
  134. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/file/__init__.py +0 -0
  135. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/file/file.py +0 -0
  136. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/file/providers/fal.py +0 -0
  137. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/file/providers/gcp.py +0 -0
  138. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/file/providers/r2.py +0 -0
  139. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/file/providers/s3.py +0 -0
  140. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/file/types.py +0 -0
  141. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/image/__init__.py +0 -0
  142. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/image/image.py +0 -0
  143. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  144. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  145. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  146. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  147. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  148. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/image/safety_checker.py +0 -0
  149. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/optimize.py +0 -0
  150. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/types.py +0 -0
  151. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/utils/__init__.py +0 -0
  152. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/utils/download_utils.py +0 -0
  153. {fal-1.13.4 → fal-1.14.0}/src/fal/toolkit/utils/retry.py +0 -0
  154. {fal-1.13.4 → fal-1.14.0}/src/fal/utils.py +0 -0
  155. {fal-1.13.4 → fal-1.14.0}/src/fal/workflows.py +0 -0
  156. {fal-1.13.4 → fal-1.14.0}/tests/__init__.py +0 -0
  157. {fal-1.13.4 → fal-1.14.0}/tests/assets/cat.png +0 -0
  158. {fal-1.13.4 → fal-1.14.0}/tests/cli/__init__.py +0 -0
  159. {fal-1.13.4 → fal-1.14.0}/tests/cli/test_apps.py +0 -0
  160. {fal-1.13.4 → fal-1.14.0}/tests/cli/test_auth.py +0 -0
  161. {fal-1.13.4 → fal-1.14.0}/tests/cli/test_deploy.py +0 -0
  162. {fal-1.13.4 → fal-1.14.0}/tests/cli/test_keys.py +0 -0
  163. {fal-1.13.4 → fal-1.14.0}/tests/cli/test_run.py +0 -0
  164. {fal-1.13.4 → fal-1.14.0}/tests/cli/test_secrets.py +0 -0
  165. {fal-1.13.4 → fal-1.14.0}/tests/conftest.py +0 -0
  166. {fal-1.13.4 → fal-1.14.0}/tests/integration_test.py +0 -0
  167. {fal-1.13.4 → fal-1.14.0}/tests/mainify_package/__init__.py +0 -0
  168. {fal-1.13.4 → fal-1.14.0}/tests/mainify_package/impl.py +0 -0
  169. {fal-1.13.4 → fal-1.14.0}/tests/mainify_package/utils.py +0 -0
  170. {fal-1.13.4 → fal-1.14.0}/tests/mainify_target.py +0 -0
  171. {fal-1.13.4 → fal-1.14.0}/tests/test_stability.py +0 -0
  172. {fal-1.13.4 → fal-1.14.0}/tests/toolkit/file_test.py +0 -0
  173. {fal-1.13.4 → fal-1.14.0}/tests/toolkit/image_test.py +0 -0
  174. {fal-1.13.4 → fal-1.14.0}/tests/toolkit/test_types.py +0 -0
  175. {fal-1.13.4 → fal-1.14.0}/tests/toolkit/utils/retry.py +0 -0
  176. {fal-1.13.4 → fal-1.14.0}/tools/demo_script.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.13.4
3
+ Version: 1.14.0
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <support@fal.ai>
6
6
  Requires-Python: >=3.8
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: isolate[build]<0.17.0,>=0.16.2
9
- Requires-Dist: isolate-proto<0.8.0,>=0.7.2
9
+ Requires-Dist: isolate-proto<0.9.0,>=0.8.1
10
10
  Requires-Dist: grpcio==1.64.0
11
11
  Requires-Dist: dill==0.3.7
12
12
  Requires-Dist: cloudpickle==3.0.0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.13.4
3
+ Version: 1.14.0
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <support@fal.ai>
6
6
  Requires-Python: >=3.8
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: isolate[build]<0.17.0,>=0.16.2
9
- Requires-Dist: isolate-proto<0.8.0,>=0.7.2
9
+ Requires-Dist: isolate-proto<0.9.0,>=0.8.1
10
10
  Requires-Dist: grpcio==1.64.0
11
11
  Requires-Dist: dill==0.3.7
12
12
  Requires-Dist: cloudpickle==3.0.0
@@ -88,6 +88,7 @@ src/fal/config.py
88
88
  src/fal/container.py
89
89
  src/fal/files.py
90
90
  src/fal/flags.py
91
+ src/fal/project.py
91
92
  src/fal/py.typed
92
93
  src/fal/rest_client.py
93
94
  src/fal/sdk.py
@@ -1,5 +1,5 @@
1
1
  isolate[build]<0.17.0,>=0.16.2
2
- isolate-proto<0.8.0,>=0.7.2
2
+ isolate-proto<0.9.0,>=0.8.1
3
3
  grpcio==1.64.0
4
4
  dill==0.3.7
5
5
  cloudpickle==3.0.0
@@ -23,7 +23,7 @@ readme = "README.md"
23
23
  requires-python = ">=3.8"
24
24
  dependencies = [
25
25
  "isolate[build]>=0.16.2,<0.17.0",
26
- "isolate-proto>=0.7.2,<0.8.0",
26
+ "isolate-proto>=0.8.1,<0.9.0",
27
27
  "grpcio==1.64.0",
28
28
  "dill==0.3.7",
29
29
  "cloudpickle==3.0.0",
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.13.4'
21
- __version_tuple__ = version_tuple = (1, 13, 4)
20
+ __version__ = version = '1.14.0'
21
+ __version_tuple__ = version_tuple = (1, 14, 0)
@@ -1119,6 +1119,11 @@ class BaseServable:
1119
1119
  request, CUDAOutOfMemoryException()
1120
1120
  )
1121
1121
 
1122
+ # last line of defense against misc GPU errors that could indicate a bad
1123
+ # worker
1124
+ if any(marker in str(exc).lower() for marker in ["cuda", "cudnn", "nvml"]):
1125
+ return JSONResponse({"detail": "GPU error"}, 503)
1126
+
1122
1127
  return JSONResponse({"detail": "Internal Server Error"}, 500)
1123
1128
 
1124
1129
  routes = self.collect_routes()
@@ -125,6 +125,26 @@ def _fetch_teams(bearer_token: str) -> list[dict]:
125
125
  raise FalServerlessException("Failed to fetch teams") from exc
126
126
 
127
127
 
128
+ def current_user_info(headers: dict[str, str]) -> dict:
129
+ import json
130
+ from urllib.error import HTTPError
131
+ from urllib.request import Request, urlopen
132
+
133
+ from fal.exceptions import FalServerlessException
134
+ from fal.flags import REST_URL
135
+
136
+ request = Request(
137
+ method="GET",
138
+ url=f"{REST_URL}/users/current",
139
+ headers=headers,
140
+ )
141
+ try:
142
+ with urlopen(request) as response:
143
+ return json.load(response)
144
+ except HTTPError as exc:
145
+ raise FalServerlessException("Failed to fetch user info") from exc
146
+
147
+
128
148
  def login(console):
129
149
  token_data = auth0.login(console)
130
150
  with local.lock_token():
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from fal.files import find_project_root, find_pyproject_toml, parse_pyproject_toml
3
+ from fal.project import find_project_root, find_pyproject_toml, parse_pyproject_toml
4
4
 
5
5
 
6
6
  def get_client(host: str, team: str | None = None):
@@ -123,7 +123,7 @@ def _app_rev_table(revs: list[ApplicationInfo]):
123
123
  def _list_rev(args):
124
124
  client = get_client(args.host, args.team)
125
125
  with client.connect() as connection:
126
- revs = connection.list_applications()
126
+ revs = connection.list_applications(args.app_name)
127
127
  table = _app_rev_table(revs)
128
128
 
129
129
  args.console.print(table)
@@ -137,6 +137,11 @@ def _add_list_rev_parser(subparsers, parents):
137
137
  help=list_help,
138
138
  parents=parents,
139
139
  )
140
+ parser.add_argument(
141
+ "app_name",
142
+ nargs="?",
143
+ help="Application name.",
144
+ )
140
145
  parser.set_defaults(func=_list_rev)
141
146
 
142
147
 
@@ -1,6 +1,10 @@
1
+ from fal.auth import current_user_info
2
+ from fal.cli import profile
3
+ from fal.sdk import get_default_credentials
4
+
5
+
1
6
  def _login(args):
2
7
  from fal.auth import login
3
- from fal.config import Config
4
8
  from fal.console.icons import CHECK_ICON, CROSS_ICON
5
9
  from fal.exceptions import FalServerlessException
6
10
 
@@ -11,15 +15,12 @@ def _login(args):
11
15
  args.console.print(f"{CROSS_ICON} {e}")
12
16
  return
13
17
 
14
- with Config().edit() as config:
15
- config.unset("team")
16
-
18
+ _unset_account(args)
17
19
  _set_account(args)
18
20
 
19
21
 
20
22
  def _logout(args):
21
23
  from fal.auth import logout
22
- from fal.config import Config
23
24
  from fal.console.icons import CHECK_ICON, CROSS_ICON
24
25
  from fal.exceptions import FalServerlessException
25
26
 
@@ -30,8 +31,7 @@ def _logout(args):
30
31
  args.console.print(f"{CROSS_ICON} {e}")
31
32
  return
32
33
 
33
- with Config().edit() as config:
34
- config.unset("team")
34
+ _unset_account(args)
35
35
 
36
36
 
37
37
  def _list_accounts(args):
@@ -43,7 +43,7 @@ def _list_accounts(args):
43
43
 
44
44
  user_access = UserAccess()
45
45
  config = Config()
46
- current_account = config.get("team") or user_access.info["nickname"]
46
+ current_account_name = config.get_internal("team") or user_access.info["nickname"]
47
47
 
48
48
  table = Table(border_style=Style(frame=False), show_header=False)
49
49
  table.add_column("#")
@@ -51,7 +51,7 @@ def _list_accounts(args):
51
51
  table.add_column("Type")
52
52
 
53
53
  for idx, account in enumerate(user_access.accounts):
54
- selected = account["nickname"] == current_account
54
+ selected = account["nickname"] == current_account_name
55
55
  color = "bold yellow" if selected else None
56
56
 
57
57
  table.add_row(
@@ -64,6 +64,13 @@ def _list_accounts(args):
64
64
  args.console.print(table)
65
65
 
66
66
 
67
+ def _unset_account(args):
68
+ from fal.config import Config
69
+
70
+ with Config().edit() as config:
71
+ config.unset_internal("team")
72
+
73
+
67
74
  def _set_account(args):
68
75
  from rich.prompt import Prompt
69
76
 
@@ -105,25 +112,24 @@ def _set_account(args):
105
112
  )
106
113
 
107
114
  with Config().edit() as config:
108
- config.set("team", account["nickname"])
109
-
115
+ config.set_internal("team", account["nickname"])
110
116
 
111
- def _whoami(args):
112
- from fal.auth import UserAccess
113
- from fal.config import Config
117
+ # Unset the profile if set
118
+ if current_profile := config.get_internal("profile"):
119
+ args.console.print(
120
+ f"\n[yellow]Unsetting profile [cyan]{current_profile}[/] "
121
+ "to make team selection effective.[/]"
122
+ )
123
+ profile._unset(args, config=config)
114
124
 
115
- user_access = UserAccess()
116
- config = Config()
117
125
 
118
- team = config.get("team")
119
- if team:
120
- account = user_access.get_account(team)
121
- else:
122
- account = user_access.get_account(user_access.info["nickname"])
126
+ def _whoami(args):
127
+ creds = get_default_credentials()
128
+ user_info = current_user_info(creds.to_headers())
123
129
 
124
- nickname = account["nickname"]
125
- full_name = account["full_name"]
126
- user_id = account["user_id"]
130
+ full_name = user_info["full_name"]
131
+ nickname = user_info["nickname"]
132
+ user_id = user_info["user_id"]
127
133
 
128
134
  args.console.print(f"Hello, {full_name}: {nickname!r} - {user_id!r}")
129
135
 
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from rich.table import Table
2
4
 
3
5
  from fal.config import Config
@@ -31,11 +33,12 @@ def _set(args):
31
33
  )
32
34
 
33
35
 
34
- def _unset(args):
35
- with Config().edit() as config:
36
- config.unset_internal("profile")
37
- args.console.print("Default profile unset.")
36
+ def _unset(args, config: Config | None = None):
37
+ config = config or Config()
38
+
39
+ with config.edit() as config:
38
40
  config.profile = None
41
+ args.console.print("Default profile unset.")
39
42
 
40
43
 
41
44
  def _key_set(args):
@@ -1,11 +1,4 @@
1
- from fal.cli.auth import _list_accounts, _set_account
2
-
3
-
4
- def _unset(args):
5
- from fal.config import Config
6
-
7
- with Config().edit() as config:
8
- config.unset("team")
1
+ from fal.cli.auth import _list_accounts, _set_account, _unset_account
9
2
 
10
3
 
11
4
  def add_parser(main_subparsers, parents):
@@ -51,4 +44,4 @@ def add_parser(main_subparsers, parents):
51
44
  help=unset_help,
52
45
  parents=parents,
53
46
  )
54
- unset_parser.set_defaults(func=_unset)
47
+ unset_parser.set_defaults(func=_unset_account)
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  from contextlib import contextmanager
3
- from typing import Dict, List, Optional
5
+ from typing import Dict, Iterator, List, Optional
4
6
 
5
7
  SETTINGS_SECTION = "__internal__" # legacy
6
8
  DEFAULT_PROFILE = "default"
@@ -9,6 +11,7 @@ DEFAULT_PROFILE = "default"
9
11
  class Config:
10
12
  _config: Dict[str, Dict[str, str]]
11
13
  _profile: Optional[str]
14
+ _editing: bool = False
12
15
 
13
16
  DEFAULT_CONFIG_PATH = "~/.fal/config.toml"
14
17
 
@@ -38,7 +41,11 @@ class Config:
38
41
  @profile.setter
39
42
  def profile(self, value: Optional[str]) -> None:
40
43
  if value and value not in self._config:
44
+ # Make sure the section exists
41
45
  self._config[value] = {}
46
+ self.set_internal("profile", value)
47
+ elif not value:
48
+ self.unset_internal("profile")
42
49
 
43
50
  self._profile = value
44
51
 
@@ -96,6 +103,12 @@ class Config:
96
103
  del self._config[profile]
97
104
 
98
105
  @contextmanager
99
- def edit(self):
100
- yield self
101
- self.save()
106
+ def edit(self) -> Iterator[Config]:
107
+ if self._editing:
108
+ # no-op
109
+ yield self
110
+ else:
111
+ self._editing = True
112
+ yield self
113
+ self.save()
114
+ self._editing = False
@@ -1,9 +1,8 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import Dict, Literal
2
+ from typing import Dict, Literal, Optional
3
3
 
4
4
  Builder = Literal["depot", "service", "worker"]
5
5
  BUILDERS = {"depot", "service", "worker"}
6
- DEFAULT_BUILDER: Builder = "depot"
7
6
  DEFAULT_COMPRESSION: str = "gzip"
8
7
  DEFAULT_FORCE_COMPRESSION: bool = False
9
8
 
@@ -17,7 +16,7 @@ class ContainerImage:
17
16
  dockerfile_str: str
18
17
  build_args: Dict[str, str] = field(default_factory=dict)
19
18
  registries: Dict[str, Dict[str, str]] = field(default_factory=dict)
20
- builder: Builder = field(default=DEFAULT_BUILDER)
19
+ builder: Optional[Builder] = field(default=None)
21
20
  compression: str = DEFAULT_COMPRESSION
22
21
  force_compression: bool = DEFAULT_FORCE_COMPRESSION
23
22
 
@@ -30,7 +29,7 @@ class ContainerImage:
30
29
  "Username and password are required for each registry"
31
30
  )
32
31
 
33
- if self.builder not in BUILDERS:
32
+ if self.builder and self.builder not in BUILDERS:
34
33
  raise ValueError(
35
34
  f"Invalid builder: {self.builder}, must be one of {BUILDERS}"
36
35
  )
@@ -0,0 +1,81 @@
1
+ from functools import lru_cache
2
+ from pathlib import Path
3
+ from typing import Any, Dict, Optional, Sequence, Tuple, Union
4
+
5
+ import tomli
6
+
7
+
8
+ @lru_cache
9
+ def _load_toml(path: Union[Path, str]) -> Dict[str, Any]:
10
+ with open(path, "rb") as f:
11
+ return tomli.load(f)
12
+
13
+
14
+ @lru_cache
15
+ def _cached_resolve(path: Path) -> Path:
16
+ return path.resolve()
17
+
18
+
19
+ @lru_cache
20
+ def find_project_root(srcs: Optional[Sequence[str]]) -> Tuple[Path, str]:
21
+ """Return a directory containing .git, or pyproject.toml.
22
+
23
+ That directory will be a common parent of all files and directories
24
+ passed in `srcs`.
25
+
26
+ If no directory in the tree contains a marker that would specify it's the
27
+ project root, the root of the file system is returned.
28
+
29
+ Returns a two-tuple with the first element as the project root path and
30
+ the second element as a string describing the method by which the
31
+ project root was discovered.
32
+ """
33
+ if not srcs:
34
+ srcs = [str(_cached_resolve(Path.cwd()))]
35
+
36
+ path_srcs = [_cached_resolve(Path(Path.cwd(), src)) for src in srcs]
37
+
38
+ # A list of lists of parents for each 'src'. 'src' is included as a
39
+ # "parent" of itself if it is a directory
40
+ src_parents = [
41
+ list(path.parents) + ([path] if path.is_dir() else []) for path in path_srcs
42
+ ]
43
+
44
+ common_base = max(
45
+ set.intersection(*(set(parents) for parents in src_parents)),
46
+ key=lambda path: path.parts,
47
+ )
48
+
49
+ for directory in (common_base, *common_base.parents):
50
+ if (directory / ".git").exists():
51
+ return directory, ".git directory"
52
+
53
+ if (directory / "pyproject.toml").is_file():
54
+ pyproject_toml = _load_toml(directory / "pyproject.toml")
55
+ if "fal" in pyproject_toml.get("tool", {}):
56
+ return directory, "pyproject.toml"
57
+
58
+ return directory, "file system root"
59
+
60
+
61
+ def find_pyproject_toml(
62
+ path_search_start: Optional[Tuple[str, ...]] = None,
63
+ ) -> Optional[str]:
64
+ """Find the absolute filepath to a pyproject.toml if it exists"""
65
+ path_project_root, _ = find_project_root(path_search_start)
66
+ path_pyproject_toml = path_project_root / "pyproject.toml"
67
+
68
+ if path_pyproject_toml.is_file():
69
+ return str(path_pyproject_toml)
70
+
71
+
72
+ def parse_pyproject_toml(path_config: str) -> Dict[str, Any]:
73
+ """Parse a pyproject toml file, pulling out relevant parts for fal.
74
+
75
+ If parsing fails, will raise a tomli.TOMLDecodeError.
76
+ """
77
+ pyproject_toml = _load_toml(path_config)
78
+ config: Dict[str, Any] = pyproject_toml.get("tool", {}).get("fal", {})
79
+ config = {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}
80
+
81
+ return config
@@ -157,7 +157,15 @@ class AuthenticatedCredentials(Credentials):
157
157
 
158
158
  def to_headers(self) -> dict[str, str]:
159
159
  token = self.user.bearer_token
160
- return {"Authorization": token}
160
+ headers = {
161
+ "Authorization": token,
162
+ }
163
+
164
+ if self.team:
165
+ team_id = self.user.get_account(self.team)["user_id"]
166
+ headers["X-Fal-User-Id"] = team_id
167
+
168
+ return headers
161
169
 
162
170
 
163
171
  @dataclass
@@ -193,7 +201,7 @@ def get_default_credentials(team: str | None = None) -> Credentials:
193
201
  return FalServerlessKeyCredentials(key_creds[0], key_creds[1])
194
202
  else:
195
203
  config = Config()
196
- team = team or config.get("team")
204
+ team = team or config.get_internal("team")
197
205
  return AuthenticatedCredentials(team=team)
198
206
 
199
207
 
@@ -650,8 +658,12 @@ class FalServerlessConnection:
650
658
  )
651
659
  return from_grpc(res.alias_info)
652
660
 
653
- def list_applications(self) -> list[ApplicationInfo]:
654
- request = isolate_proto.ListApplicationsRequest()
661
+ def list_applications(
662
+ self, application_name: str | None = None
663
+ ) -> list[ApplicationInfo]:
664
+ request = isolate_proto.ListApplicationsRequest(
665
+ application_name=application_name
666
+ )
655
667
  res: isolate_proto.ListApplicationsResult = self.stub.ListApplications(request)
656
668
  return [from_grpc(app) for app in res.applications]
657
669