fal 1.44.1__tar.gz → 1.45.1__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 (190) hide show
  1. {fal-1.44.1/fal.egg-info → fal-1.45.1}/PKG-INFO +2 -1
  2. {fal-1.44.1 → fal-1.45.1/fal.egg-info}/PKG-INFO +2 -1
  3. {fal-1.44.1 → fal-1.45.1}/fal.egg-info/SOURCES.txt +3 -0
  4. {fal-1.44.1 → fal-1.45.1}/fal.egg-info/requires.txt +1 -0
  5. {fal-1.44.1 → fal-1.45.1}/pyproject.toml +1 -0
  6. {fal-1.44.1 → fal-1.45.1}/src/fal/_fal_version.py +2 -2
  7. {fal-1.44.1 → fal-1.45.1}/src/fal/api.py +50 -3
  8. {fal-1.44.1 → fal-1.45.1}/src/fal/app.py +29 -1
  9. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/deploy.py +2 -1
  10. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/run.py +6 -2
  11. {fal-1.44.1 → fal-1.45.1}/src/fal/exceptions/__init__.py +2 -0
  12. {fal-1.44.1 → fal-1.45.1}/src/fal/exceptions/_base.py +15 -0
  13. fal-1.45.1/src/fal/file_sync.py +361 -0
  14. {fal-1.44.1 → fal-1.45.1}/src/fal/sdk.py +21 -1
  15. {fal-1.44.1 → fal-1.45.1}/src/fal/utils.py +1 -0
  16. {fal-1.44.1 → fal-1.45.1}/tests/cli/test_deploy.py +12 -4
  17. fal-1.45.1/tests/test_file_sync.py +316 -0
  18. fal-1.45.1/tests/unit/test_app.py +64 -0
  19. {fal-1.44.1 → fal-1.45.1}/.gitignore +0 -0
  20. {fal-1.44.1 → fal-1.45.1}/Makefile +0 -0
  21. {fal-1.44.1 → fal-1.45.1}/README.md +0 -0
  22. {fal-1.44.1 → fal-1.45.1}/docs/conf.py +0 -0
  23. {fal-1.44.1 → fal-1.45.1}/docs/index.rst +0 -0
  24. {fal-1.44.1 → fal-1.45.1}/fal.egg-info/dependency_links.txt +0 -0
  25. {fal-1.44.1 → fal-1.45.1}/fal.egg-info/entry_points.txt +0 -0
  26. {fal-1.44.1 → fal-1.45.1}/fal.egg-info/top_level.txt +0 -0
  27. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/README.md +0 -0
  28. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  29. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  30. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  31. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  32. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  33. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  34. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  35. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  36. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  37. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  38. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  39. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  40. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  41. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  42. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  43. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  44. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  45. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  46. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  47. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  48. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  49. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  50. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  51. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  52. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  53. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  54. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  55. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  56. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  57. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  58. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  59. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  60. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  61. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  62. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  63. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  64. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  65. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  66. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  67. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  68. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  69. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  70. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  71. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  72. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  73. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  74. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  75. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  76. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  77. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  78. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  79. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  80. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  81. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  82. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  83. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  84. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  85. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  86. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  87. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  88. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  89. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  90. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  91. {fal-1.44.1 → fal-1.45.1}/openapi-fal-rest/pyproject.toml +0 -0
  92. {fal-1.44.1 → fal-1.45.1}/openapi_rest.config.yaml +0 -0
  93. {fal-1.44.1 → fal-1.45.1}/setup.cfg +0 -0
  94. {fal-1.44.1 → fal-1.45.1}/src/fal/__init__.py +0 -0
  95. {fal-1.44.1 → fal-1.45.1}/src/fal/__main__.py +0 -0
  96. {fal-1.44.1 → fal-1.45.1}/src/fal/_serialization.py +0 -0
  97. {fal-1.44.1 → fal-1.45.1}/src/fal/_version.py +0 -0
  98. {fal-1.44.1 → fal-1.45.1}/src/fal/apps.py +0 -0
  99. {fal-1.44.1 → fal-1.45.1}/src/fal/auth/__init__.py +0 -0
  100. {fal-1.44.1 → fal-1.45.1}/src/fal/auth/auth0.py +0 -0
  101. {fal-1.44.1 → fal-1.45.1}/src/fal/auth/local.py +0 -0
  102. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/__init__.py +0 -0
  103. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/_utils.py +0 -0
  104. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/api.py +0 -0
  105. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/apps.py +0 -0
  106. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/auth.py +0 -0
  107. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/cli_nested_json.py +0 -0
  108. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/create.py +0 -0
  109. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/debug.py +0 -0
  110. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/doctor.py +0 -0
  111. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/files.py +0 -0
  112. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/keys.py +0 -0
  113. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/main.py +0 -0
  114. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/parser.py +0 -0
  115. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/profile.py +0 -0
  116. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/queue.py +0 -0
  117. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/runners.py +0 -0
  118. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/secrets.py +0 -0
  119. {fal-1.44.1 → fal-1.45.1}/src/fal/cli/teams.py +0 -0
  120. {fal-1.44.1 → fal-1.45.1}/src/fal/config.py +0 -0
  121. {fal-1.44.1 → fal-1.45.1}/src/fal/console/__init__.py +0 -0
  122. {fal-1.44.1 → fal-1.45.1}/src/fal/console/icons.py +0 -0
  123. {fal-1.44.1 → fal-1.45.1}/src/fal/console/ux.py +0 -0
  124. {fal-1.44.1 → fal-1.45.1}/src/fal/container.py +0 -0
  125. {fal-1.44.1 → fal-1.45.1}/src/fal/exceptions/_cuda.py +0 -0
  126. {fal-1.44.1 → fal-1.45.1}/src/fal/exceptions/auth.py +0 -0
  127. {fal-1.44.1 → fal-1.45.1}/src/fal/files.py +0 -0
  128. {fal-1.44.1 → fal-1.45.1}/src/fal/flags.py +0 -0
  129. {fal-1.44.1 → fal-1.45.1}/src/fal/logging/__init__.py +0 -0
  130. {fal-1.44.1 → fal-1.45.1}/src/fal/logging/isolate.py +0 -0
  131. {fal-1.44.1 → fal-1.45.1}/src/fal/logging/style.py +0 -0
  132. {fal-1.44.1 → fal-1.45.1}/src/fal/logging/trace.py +0 -0
  133. {fal-1.44.1 → fal-1.45.1}/src/fal/project.py +0 -0
  134. {fal-1.44.1 → fal-1.45.1}/src/fal/py.typed +0 -0
  135. {fal-1.44.1 → fal-1.45.1}/src/fal/rest_client.py +0 -0
  136. {fal-1.44.1 → fal-1.45.1}/src/fal/sync.py +0 -0
  137. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/__init__.py +0 -0
  138. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/audio/__init__.py +0 -0
  139. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/audio/audio.py +0 -0
  140. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/exceptions.py +0 -0
  141. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/file/__init__.py +0 -0
  142. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/file/file.py +0 -0
  143. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/file/providers/fal.py +0 -0
  144. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/file/providers/gcp.py +0 -0
  145. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/file/providers/r2.py +0 -0
  146. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/file/providers/s3.py +0 -0
  147. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/file/types.py +0 -0
  148. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/image/__init__.py +0 -0
  149. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/image/image.py +0 -0
  150. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  151. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  152. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  153. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  154. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  155. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/image/safety_checker.py +0 -0
  156. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/kv.py +0 -0
  157. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/optimize.py +0 -0
  158. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/types.py +0 -0
  159. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/utils/__init__.py +0 -0
  160. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/utils/download_utils.py +0 -0
  161. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/utils/endpoint.py +0 -0
  162. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/utils/retry.py +0 -0
  163. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/utils/setup_utils.py +0 -0
  164. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/video/__init__.py +0 -0
  165. {fal-1.44.1 → fal-1.45.1}/src/fal/toolkit/video/video.py +0 -0
  166. {fal-1.44.1 → fal-1.45.1}/src/fal/workflows.py +0 -0
  167. {fal-1.44.1 → fal-1.45.1}/tests/__init__.py +0 -0
  168. {fal-1.44.1 → fal-1.45.1}/tests/assets/cat.png +0 -0
  169. {fal-1.44.1 → fal-1.45.1}/tests/cli/__init__.py +0 -0
  170. {fal-1.44.1 → fal-1.45.1}/tests/cli/test_apps.py +0 -0
  171. {fal-1.44.1 → fal-1.45.1}/tests/cli/test_auth.py +0 -0
  172. {fal-1.44.1 → fal-1.45.1}/tests/cli/test_keys.py +0 -0
  173. {fal-1.44.1 → fal-1.45.1}/tests/cli/test_run.py +0 -0
  174. {fal-1.44.1 → fal-1.45.1}/tests/cli/test_secrets.py +0 -0
  175. {fal-1.44.1 → fal-1.45.1}/tests/conftest.py +0 -0
  176. {fal-1.44.1 → fal-1.45.1}/tests/integration_test.py +0 -0
  177. {fal-1.44.1 → fal-1.45.1}/tests/mainify_package/__init__.py +0 -0
  178. {fal-1.44.1 → fal-1.45.1}/tests/mainify_package/impl.py +0 -0
  179. {fal-1.44.1 → fal-1.45.1}/tests/mainify_package/utils.py +0 -0
  180. {fal-1.44.1 → fal-1.45.1}/tests/mainify_target.py +0 -0
  181. {fal-1.44.1 → fal-1.45.1}/tests/test_apps.py +0 -0
  182. {fal-1.44.1 → fal-1.45.1}/tests/test_files.py +0 -0
  183. {fal-1.44.1 → fal-1.45.1}/tests/test_kv.py +0 -0
  184. {fal-1.44.1 → fal-1.45.1}/tests/test_stability.py +0 -0
  185. {fal-1.44.1 → fal-1.45.1}/tests/toolkit/file/providers/test_fal_retry.py +0 -0
  186. {fal-1.44.1 → fal-1.45.1}/tests/toolkit/file_test.py +0 -0
  187. {fal-1.44.1 → fal-1.45.1}/tests/toolkit/image_test.py +0 -0
  188. {fal-1.44.1 → fal-1.45.1}/tests/toolkit/test_types.py +0 -0
  189. {fal-1.44.1 → fal-1.45.1}/tests/toolkit/utils/retry.py +0 -0
  190. {fal-1.44.1 → fal-1.45.1}/tools/demo_script.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.44.1
3
+ Version: 1.45.1
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
@@ -31,6 +31,7 @@ Requires-Dist: python-dateutil<3,>=2.8.0
31
31
  Requires-Dist: types-python-dateutil<3,>=2.8.0
32
32
  Requires-Dist: dateparser<2,>=1.2.0
33
33
  Requires-Dist: types-dateparser<2,>=1.2.0
34
+ Requires-Dist: tuspy==1.1.0
34
35
  Requires-Dist: importlib-metadata>=4.4; python_version < "3.10"
35
36
  Requires-Dist: msgpack<2,>=1.0.7
36
37
  Requires-Dist: websockets>=12.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.44.1
3
+ Version: 1.45.1
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
@@ -31,6 +31,7 @@ Requires-Dist: python-dateutil<3,>=2.8.0
31
31
  Requires-Dist: types-python-dateutil<3,>=2.8.0
32
32
  Requires-Dist: dateparser<2,>=1.2.0
33
33
  Requires-Dist: types-dateparser<2,>=1.2.0
34
+ Requires-Dist: tuspy==1.1.0
34
35
  Requires-Dist: importlib-metadata>=4.4; python_version < "3.10"
35
36
  Requires-Dist: msgpack<2,>=1.0.7
36
37
  Requires-Dist: websockets>=12.0
@@ -86,6 +86,7 @@ src/fal/app.py
86
86
  src/fal/apps.py
87
87
  src/fal/config.py
88
88
  src/fal/container.py
89
+ src/fal/file_sync.py
89
90
  src/fal/files.py
90
91
  src/fal/flags.py
91
92
  src/fal/project.py
@@ -163,6 +164,7 @@ tests/conftest.py
163
164
  tests/integration_test.py
164
165
  tests/mainify_target.py
165
166
  tests/test_apps.py
167
+ tests/test_file_sync.py
166
168
  tests/test_files.py
167
169
  tests/test_kv.py
168
170
  tests/test_stability.py
@@ -182,4 +184,5 @@ tests/toolkit/image_test.py
182
184
  tests/toolkit/test_types.py
183
185
  tests/toolkit/file/providers/test_fal_retry.py
184
186
  tests/toolkit/utils/retry.py
187
+ tests/unit/test_app.py
185
188
  tools/demo_script.py
@@ -24,6 +24,7 @@ python-dateutil<3,>=2.8.0
24
24
  types-python-dateutil<3,>=2.8.0
25
25
  dateparser<2,>=1.2.0
26
26
  types-dateparser<2,>=1.2.0
27
+ tuspy==1.1.0
27
28
  msgpack<2,>=1.0.7
28
29
  websockets>=12.0
29
30
  pillow<12,>=10.2.0
@@ -50,6 +50,7 @@ dependencies = [
50
50
  "types-python-dateutil>=2.8.0,<3",
51
51
  "dateparser>=1.2.0,<2",
52
52
  "types-dateparser>=1.2.0,<2",
53
+ "tuspy==1.1.0",
53
54
  # For 3.9 and earlier, importlib-metadata's newer versions are included in the standard library.
54
55
  'importlib-metadata>=4.4; python_version < "3.10"',
55
56
  "msgpack>=1.0.7,<2",
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.44.1'
32
- __version_tuple__ = version_tuple = (1, 44, 1)
31
+ __version__ = version = '1.45.1'
32
+ __version_tuple__ = version_tuple = (1, 45, 1)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -43,6 +43,7 @@ from typing_extensions import Concatenate, ParamSpec
43
43
 
44
44
  import fal.flags as flags
45
45
  from fal._serialization import include_module, include_modules_from, patch_pickle
46
+ from fal.console import console
46
47
  from fal.container import ContainerImage
47
48
  from fal.exceptions import (
48
49
  AppException,
@@ -51,6 +52,7 @@ from fal.exceptions import (
51
52
  FieldException,
52
53
  )
53
54
  from fal.exceptions._cuda import _is_cuda_oom_exception
55
+ from fal.file_sync import FileSync
54
56
  from fal.logging.isolate import IsolateLogPrinter
55
57
  from fal.sdk import (
56
58
  FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER,
@@ -63,6 +65,7 @@ from fal.sdk import (
63
65
  DeploymentStrategyLiteral,
64
66
  FalServerlessClient,
65
67
  FalServerlessConnection,
68
+ File,
66
69
  HostedRunState,
67
70
  MachineRequirements,
68
71
  get_agent_credentials,
@@ -438,16 +441,25 @@ class FalServerlessHost(Host):
438
441
  "_base_image",
439
442
  "_scheduler",
440
443
  "_scheduler_options",
444
+ "app_files",
445
+ "app_files_ignore",
446
+ "app_files_context_dir",
441
447
  }
442
448
  )
443
449
 
444
450
  url: str = FAL_SERVERLESS_DEFAULT_URL
451
+ local_file_path: str = ""
445
452
  credentials: Credentials = field(default_factory=get_default_credentials)
446
- _lock: threading.Lock = field(default_factory=threading.Lock)
447
453
 
448
- _log_printer = IsolateLogPrinter(debug=flags.DEBUG)
454
+ _lock: threading.Lock = field(default_factory=threading.Lock, init=False)
455
+
456
+ _log_printer: IsolateLogPrinter = field(
457
+ default_factory=lambda: IsolateLogPrinter(debug=flags.DEBUG), init=False
458
+ )
449
459
 
450
- _thread_pool: ThreadPoolExecutor = field(default_factory=ThreadPoolExecutor)
460
+ _thread_pool: ThreadPoolExecutor = field(
461
+ default_factory=ThreadPoolExecutor, init=False
462
+ )
451
463
 
452
464
  def __getstate__(self) -> dict[str, Any]:
453
465
  state = self.__dict__.copy()
@@ -465,6 +477,35 @@ class FalServerlessHost(Host):
465
477
  client = FalServerlessClient(self.url, self.credentials)
466
478
  return client.connect()
467
479
 
480
+ def _app_files_sync(self, options: Options) -> list[File]:
481
+ import re # noqa: PLC0415
482
+
483
+ app_files: list[str] = options.host.get("app_files", [])
484
+ app_files_ignore_str = options.host.get("app_files_ignore", [])
485
+ app_files_ignore = [re.compile(pattern) for pattern in app_files_ignore_str]
486
+ app_files_context_dir = options.host.get("app_files_context_dir", None)
487
+ res = []
488
+ if app_files:
489
+ sync = FileSync(self.local_file_path)
490
+ files, errors = sync.sync_files(
491
+ app_files,
492
+ files_ignore=app_files_ignore,
493
+ files_context_dir=app_files_context_dir,
494
+ )
495
+ if errors:
496
+ for error in errors:
497
+ console.print(
498
+ f"Error uploading file {error.relative_path}: {error.message}"
499
+ )
500
+
501
+ raise FalServerlessException("Error uploading files")
502
+
503
+ res = [
504
+ File(relative_path=file.relative_path, hash=file.hash) for file in files
505
+ ]
506
+
507
+ return res
508
+
468
509
  @_handle_grpc_error()
469
510
  def register(
470
511
  self,
@@ -515,6 +556,8 @@ class FalServerlessHost(Host):
515
556
  startup_timeout=startup_timeout,
516
557
  )
517
558
 
559
+ app_files = self._app_files_sync(options)
560
+
518
561
  partial_func = _prepare_partial_func(func)
519
562
 
520
563
  if metadata is None:
@@ -537,6 +580,7 @@ class FalServerlessHost(Host):
537
580
  scale=scale,
538
581
  # By default, logs are public
539
582
  private_logs=options.host.get("private_logs", False),
583
+ files=app_files,
540
584
  ):
541
585
  for log in partial_result.logs:
542
586
  self._log_printer.print(log)
@@ -594,6 +638,8 @@ class FalServerlessHost(Host):
594
638
  startup_timeout=startup_timeout,
595
639
  )
596
640
 
641
+ app_files = self._app_files_sync(options)
642
+
597
643
  return_value = _UNSET
598
644
  # Allow isolate provided arguments (such as setup function) to take
599
645
  # precedence over the ones provided by the user.
@@ -603,6 +649,7 @@ class FalServerlessHost(Host):
603
649
  environments,
604
650
  machine_requirements=machine_requirements,
605
651
  setup_function=setup_function,
652
+ files=app_files,
606
653
  ):
607
654
  result_handler(partial_result)
608
655
 
@@ -35,6 +35,13 @@ from fal.toolkit.file.providers.fal import LIFECYCLE_PREFERENCE
35
35
 
36
36
  REALTIME_APP_REQUIREMENTS = ["websockets", "msgpack"]
37
37
  REQUEST_ID_KEY = "x-fal-request-id"
38
+ DEFAULT_APP_FILES_IGNORE = [
39
+ r"\.pyc$",
40
+ r"__pycache__/",
41
+ r"\.git/",
42
+ r"\.DS_Store$",
43
+ ]
44
+
38
45
 
39
46
  EndpointT = TypeVar("EndpointT", bound=Callable[..., Any])
40
47
  logger = get_logger(__name__)
@@ -298,6 +305,14 @@ def _print_python_packages() -> None:
298
305
  print("[debug] Python packages installed:", ", ".join(packages))
299
306
 
300
307
 
308
+ def _include_app_files_path():
309
+ import sys # noqa: PLC0415
310
+
311
+ # Add local files deployment path to sys.path so imports
312
+ # work correctly in the isolate agent
313
+ sys.path.append("/app_files")
314
+
315
+
301
316
  class App(BaseServable):
302
317
  requirements: ClassVar[list[str]] = []
303
318
  local_python_modules: ClassVar[list[str]] = []
@@ -313,6 +328,9 @@ class App(BaseServable):
313
328
  }
314
329
  app_name: ClassVar[Optional[str]] = None
315
330
  app_auth: ClassVar[Optional[AuthModeLiteral]] = None
331
+ app_files: ClassVar[list[str]] = []
332
+ app_files_ignore: ClassVar[list[str]] = DEFAULT_APP_FILES_IGNORE
333
+ app_files_context_dir: ClassVar[Optional[str]] = None
316
334
  request_timeout: ClassVar[Optional[int]] = None
317
335
  startup_timeout: ClassVar[Optional[int]] = None
318
336
  min_concurrency: ClassVar[Optional[int]] = None
@@ -334,6 +352,15 @@ class App(BaseServable):
334
352
  if cls.startup_timeout is not None:
335
353
  cls.host_kwargs["startup_timeout"] = cls.startup_timeout
336
354
 
355
+ if cls.app_files:
356
+ cls.host_kwargs["app_files"] = cls.app_files
357
+
358
+ if cls.app_files_ignore:
359
+ cls.host_kwargs["app_files_ignore"] = cls.app_files_ignore
360
+
361
+ if cls.app_files_context_dir is not None:
362
+ cls.host_kwargs["app_files_context_dir"] = cls.app_files_context_dir
363
+
337
364
  if cls.min_concurrency is not None:
338
365
  cls.host_kwargs["min_concurrency"] = cls.min_concurrency
339
366
 
@@ -349,7 +376,7 @@ class App(BaseServable):
349
376
  if cls.max_multiplexing is not None:
350
377
  cls.host_kwargs["max_multiplexing"] = cls.max_multiplexing
351
378
 
352
- cls.app_name = getattr(cls, "app_name", app_name)
379
+ cls.app_name = getattr(cls, "app_name") or app_name
353
380
 
354
381
  if cls.__init__ is not App.__init__:
355
382
  raise ValueError(
@@ -382,6 +409,7 @@ class App(BaseServable):
382
409
 
383
410
  @asynccontextmanager
384
411
  async def lifespan(self, app: fastapi.FastAPI):
412
+ _include_app_files_path()
385
413
  _print_python_packages()
386
414
  await _call_any_fn(self.setup)
387
415
  try:
@@ -92,7 +92,7 @@ def _deploy_from_reference(
92
92
  file_path = str(file_path) # type: ignore
93
93
 
94
94
  user = _get_user()
95
- host = FalServerlessHost(args.host)
95
+ host = FalServerlessHost(args.host, local_file_path=str(file_path))
96
96
  loaded = load_function_from(
97
97
  host,
98
98
  file_path, # type: ignore
@@ -180,6 +180,7 @@ def _deploy(args):
180
180
  # default comes from the CLI
181
181
  app_deployment_strategy = cast(DeploymentStrategyLiteral, args.strategy)
182
182
  app_scale_settings = cast(bool, args.app_scale_settings)
183
+ file_path = str(Path(file_path).absolute())
183
184
 
184
185
  _deploy_from_reference(
185
186
  (file_path, func_name),
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  from ._utils import get_app_data_from_toml, is_app_name
2
4
  from .parser import FalClientParser, RefAction
3
5
 
@@ -6,14 +8,16 @@ def _run(args):
6
8
  from fal.api import FalServerlessHost
7
9
  from fal.utils import load_function_from
8
10
 
9
- host = FalServerlessHost(args.host)
10
-
11
11
  if is_app_name(args.func_ref):
12
12
  app_name = args.func_ref[0]
13
13
  app_ref, *_ = get_app_data_from_toml(app_name)
14
14
  file_path, func_name = RefAction.split_ref(app_ref)
15
15
  else:
16
16
  file_path, func_name = args.func_ref
17
+ # Turn relative path into absolute path for files
18
+ file_path = str(Path(file_path).absolute())
19
+
20
+ host = FalServerlessHost(args.host, local_file_path=file_path)
17
21
 
18
22
  loaded = load_function_from(host, file_path, func_name)
19
23
 
@@ -2,8 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  from ._base import (
4
4
  AppException, # noqa: F401
5
+ AppFileUploadException, # noqa: F401
5
6
  FalServerlessException, # noqa: F401
6
7
  FieldException, # noqa: F401
8
+ FileTooLargeError, # noqa: F401
7
9
  RequestCancelledException, # noqa: F401
8
10
  )
9
11
  from ._cuda import CUDAOutOfMemoryException # noqa: F401
@@ -56,3 +56,18 @@ class RequestCancelledException(FalServerlessException):
56
56
  """Exception raised when the request is cancelled by the client."""
57
57
 
58
58
  message: str = "Request cancelled by the client."
59
+
60
+
61
+ @dataclass
62
+ class FileTooLargeError(FalServerlessException):
63
+ """Exception raised when the file is too large."""
64
+
65
+ message: str = "File is too large."
66
+
67
+
68
+ @dataclass
69
+ class AppFileUploadException(FalServerlessException):
70
+ """Raised when file upload fails"""
71
+
72
+ message: str
73
+ relative_path: str