fal 1.59.0__tar.gz → 1.61.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.
Files changed (205) hide show
  1. {fal-1.59.0/fal.egg-info → fal-1.61.0}/PKG-INFO +3 -3
  2. {fal-1.59.0 → fal-1.61.0/fal.egg-info}/PKG-INFO +3 -3
  3. {fal-1.59.0 → fal-1.61.0}/fal.egg-info/SOURCES.txt +1 -0
  4. {fal-1.59.0 → fal-1.61.0}/fal.egg-info/requires.txt +2 -2
  5. {fal-1.59.0 → fal-1.61.0}/pyproject.toml +2 -2
  6. {fal-1.59.0 → fal-1.61.0}/src/fal/_fal_version.py +2 -2
  7. fal-1.61.0/src/fal/api/__init__.py +16 -0
  8. {fal-1.59.0 → fal-1.61.0}/src/fal/api/api.py +70 -56
  9. fal-1.61.0/src/fal/api/client.py +405 -0
  10. {fal-1.59.0 → fal-1.61.0}/src/fal/api/deploy.py +4 -0
  11. {fal-1.59.0 → fal-1.61.0}/src/fal/app.py +93 -4
  12. {fal-1.59.0 → fal-1.61.0}/src/fal/apps.py +1 -1
  13. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/apps.py +3 -2
  14. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/deploy.py +19 -9
  15. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/queue.py +14 -2
  16. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/run.py +9 -2
  17. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/runners.py +12 -0
  18. fal-1.61.0/src/fal/exceptions/__init__.py +23 -0
  19. {fal-1.59.0 → fal-1.61.0}/src/fal/exceptions/_base.py +1 -0
  20. {fal-1.59.0 → fal-1.61.0}/src/fal/file_sync.py +23 -30
  21. {fal-1.59.0 → fal-1.61.0}/src/fal/files.py +21 -58
  22. {fal-1.59.0 → fal-1.61.0}/src/fal/flags.py +4 -0
  23. {fal-1.59.0 → fal-1.61.0}/src/fal/sdk.py +22 -11
  24. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/__init__.py +21 -5
  25. fal-1.61.0/src/fal/upload.py +313 -0
  26. {fal-1.59.0 → fal-1.61.0}/src/fal/utils.py +3 -1
  27. {fal-1.59.0 → fal-1.61.0}/tests/e2e/test_apps.py +292 -19
  28. {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_deploy.py +35 -0
  29. {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_run.py +19 -4
  30. {fal-1.59.0 → fal-1.61.0}/tests/unit/test_app.py +3 -1
  31. {fal-1.59.0 → fal-1.61.0}/tests/unit/test_file_sync.py +122 -14
  32. fal-1.59.0/src/fal/api/__init__.py +0 -2
  33. fal-1.59.0/src/fal/api/client.py +0 -194
  34. fal-1.59.0/src/fal/exceptions/__init__.py +0 -11
  35. {fal-1.59.0 → fal-1.61.0}/.gitignore +0 -0
  36. {fal-1.59.0 → fal-1.61.0}/Makefile +0 -0
  37. {fal-1.59.0 → fal-1.61.0}/README.md +0 -0
  38. {fal-1.59.0 → fal-1.61.0}/docs/conf.py +0 -0
  39. {fal-1.59.0 → fal-1.61.0}/docs/index.rst +0 -0
  40. {fal-1.59.0 → fal-1.61.0}/fal.egg-info/dependency_links.txt +0 -0
  41. {fal-1.59.0 → fal-1.61.0}/fal.egg-info/entry_points.txt +0 -0
  42. {fal-1.59.0 → fal-1.61.0}/fal.egg-info/top_level.txt +0 -0
  43. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/README.md +0 -0
  44. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  45. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  46. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  47. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  48. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  49. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  50. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  51. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  52. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  53. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  54. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  55. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  56. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  57. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  58. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  59. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  60. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  61. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  62. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  63. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  64. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  65. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  66. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  67. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  68. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  69. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  70. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  71. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  72. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  73. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  74. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  75. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  76. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  77. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  78. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  79. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  80. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  81. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  82. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  83. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  84. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  85. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  86. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  87. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  88. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  89. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  90. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  91. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  92. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  93. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  94. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  95. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  96. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  97. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  98. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  99. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  100. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  101. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  102. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  103. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  104. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  105. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  106. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  107. {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/pyproject.toml +0 -0
  108. {fal-1.59.0 → fal-1.61.0}/openapi_rest.config.yaml +0 -0
  109. {fal-1.59.0 → fal-1.61.0}/setup.cfg +0 -0
  110. {fal-1.59.0 → fal-1.61.0}/src/fal/__init__.py +0 -0
  111. {fal-1.59.0 → fal-1.61.0}/src/fal/__main__.py +0 -0
  112. {fal-1.59.0 → fal-1.61.0}/src/fal/_serialization.py +0 -0
  113. {fal-1.59.0 → fal-1.61.0}/src/fal/_version.py +0 -0
  114. {fal-1.59.0 → fal-1.61.0}/src/fal/api/apps.py +0 -0
  115. {fal-1.59.0 → fal-1.61.0}/src/fal/api/keys.py +0 -0
  116. {fal-1.59.0 → fal-1.61.0}/src/fal/api/runners.py +0 -0
  117. {fal-1.59.0 → fal-1.61.0}/src/fal/api/secrets.py +0 -0
  118. {fal-1.59.0 → fal-1.61.0}/src/fal/auth/__init__.py +0 -0
  119. {fal-1.59.0 → fal-1.61.0}/src/fal/auth/auth0.py +0 -0
  120. {fal-1.59.0 → fal-1.61.0}/src/fal/auth/local.py +0 -0
  121. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/__init__.py +0 -0
  122. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/_utils.py +0 -0
  123. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/api.py +0 -0
  124. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/auth.py +0 -0
  125. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/cli_nested_json.py +0 -0
  126. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/create.py +0 -0
  127. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/debug.py +0 -0
  128. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/doctor.py +0 -0
  129. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/files.py +0 -0
  130. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/keys.py +0 -0
  131. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/main.py +0 -0
  132. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/parser.py +0 -0
  133. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/profile.py +0 -0
  134. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/secrets.py +0 -0
  135. {fal-1.59.0 → fal-1.61.0}/src/fal/cli/teams.py +0 -0
  136. {fal-1.59.0 → fal-1.61.0}/src/fal/config.py +0 -0
  137. {fal-1.59.0 → fal-1.61.0}/src/fal/console/__init__.py +0 -0
  138. {fal-1.59.0 → fal-1.61.0}/src/fal/console/icons.py +0 -0
  139. {fal-1.59.0 → fal-1.61.0}/src/fal/console/ux.py +0 -0
  140. {fal-1.59.0 → fal-1.61.0}/src/fal/container.py +0 -0
  141. {fal-1.59.0 → fal-1.61.0}/src/fal/distributed/__init__.py +0 -0
  142. {fal-1.59.0 → fal-1.61.0}/src/fal/distributed/utils.py +0 -0
  143. {fal-1.59.0 → fal-1.61.0}/src/fal/distributed/worker.py +0 -0
  144. {fal-1.59.0 → fal-1.61.0}/src/fal/exceptions/_cuda.py +0 -0
  145. {fal-1.59.0 → fal-1.61.0}/src/fal/exceptions/auth.py +0 -0
  146. {fal-1.59.0 → fal-1.61.0}/src/fal/logging/__init__.py +0 -0
  147. {fal-1.59.0 → fal-1.61.0}/src/fal/logging/isolate.py +0 -0
  148. {fal-1.59.0 → fal-1.61.0}/src/fal/logging/style.py +0 -0
  149. {fal-1.59.0 → fal-1.61.0}/src/fal/logging/trace.py +0 -0
  150. {fal-1.59.0 → fal-1.61.0}/src/fal/project.py +0 -0
  151. {fal-1.59.0 → fal-1.61.0}/src/fal/py.typed +0 -0
  152. {fal-1.59.0 → fal-1.61.0}/src/fal/sync.py +0 -0
  153. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/audio/__init__.py +0 -0
  154. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/audio/audio.py +0 -0
  155. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/compilation.py +0 -0
  156. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/exceptions.py +0 -0
  157. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/__init__.py +0 -0
  158. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/file.py +0 -0
  159. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/providers/fal.py +0 -0
  160. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/providers/gcp.py +0 -0
  161. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/providers/r2.py +0 -0
  162. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/providers/s3.py +0 -0
  163. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/types.py +0 -0
  164. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/__init__.py +0 -0
  165. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/image.py +0 -0
  166. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  167. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  168. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  169. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  170. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  171. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/safety_checker.py +0 -0
  172. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/kv.py +0 -0
  173. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/optimize.py +0 -0
  174. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/types.py +0 -0
  175. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/__init__.py +0 -0
  176. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/download_utils.py +0 -0
  177. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/endpoint.py +0 -0
  178. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/retry.py +0 -0
  179. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/setup_utils.py +0 -0
  180. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/video/__init__.py +0 -0
  181. {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/video/video.py +0 -0
  182. {fal-1.59.0 → fal-1.61.0}/src/fal/workflows.py +0 -0
  183. {fal-1.59.0 → fal-1.61.0}/tests/__init__.py +0 -0
  184. {fal-1.59.0 → fal-1.61.0}/tests/assets/cat.png +0 -0
  185. {fal-1.59.0 → fal-1.61.0}/tests/conftest.py +0 -0
  186. {fal-1.59.0 → fal-1.61.0}/tests/integration/test.py +0 -0
  187. {fal-1.59.0 → fal-1.61.0}/tests/integration/test_files.py +0 -0
  188. {fal-1.59.0 → fal-1.61.0}/tests/integration/test_stability.py +0 -0
  189. {fal-1.59.0 → fal-1.61.0}/tests/integration/toolkit/test_image.py +0 -0
  190. {fal-1.59.0 → fal-1.61.0}/tests/integration/toolkit/test_kv.py +0 -0
  191. {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/__init__.py +0 -0
  192. {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_apps.py +0 -0
  193. {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_auth.py +0 -0
  194. {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_keys.py +0 -0
  195. {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_secrets.py +0 -0
  196. {fal-1.59.0 → fal-1.61.0}/tests/unit/distributed/__init__.py +0 -0
  197. {fal-1.59.0 → fal-1.61.0}/tests/unit/distributed/test_integration.py +0 -0
  198. {fal-1.59.0 → fal-1.61.0}/tests/unit/distributed/test_utils.py +0 -0
  199. {fal-1.59.0 → fal-1.61.0}/tests/unit/distributed/test_worker.py +0 -0
  200. {fal-1.59.0 → fal-1.61.0}/tests/unit/test_utils.py +0 -0
  201. {fal-1.59.0 → fal-1.61.0}/tests/unit/toolkit/file/providers/test_fal_retry.py +0 -0
  202. {fal-1.59.0 → fal-1.61.0}/tests/unit/toolkit/test_file.py +0 -0
  203. {fal-1.59.0 → fal-1.61.0}/tests/unit/toolkit/test_types.py +0 -0
  204. {fal-1.59.0 → fal-1.61.0}/tests/unit/toolkit/utils/test_retry.py +0 -0
  205. {fal-1.59.0 → fal-1.61.0}/tools/demo_script.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.59.0
3
+ Version: 1.61.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
- Requires-Dist: isolate[build]<0.22.0,>=0.18.0
9
- Requires-Dist: isolate-proto>=0.29.2
8
+ Requires-Dist: isolate[build]<0.23.0,>=0.22.0
9
+ Requires-Dist: isolate-proto>=0.29.4
10
10
  Requires-Dist: grpcio<2,>=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.59.0
3
+ Version: 1.61.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
- Requires-Dist: isolate[build]<0.22.0,>=0.18.0
9
- Requires-Dist: isolate-proto>=0.29.2
8
+ Requires-Dist: isolate[build]<0.23.0,>=0.22.0
9
+ Requires-Dist: isolate-proto>=0.29.4
10
10
  Requires-Dist: grpcio<2,>=1.64.0
11
11
  Requires-Dist: dill==0.3.7
12
12
  Requires-Dist: cloudpickle==3.0.0
@@ -92,6 +92,7 @@ src/fal/project.py
92
92
  src/fal/py.typed
93
93
  src/fal/sdk.py
94
94
  src/fal/sync.py
95
+ src/fal/upload.py
95
96
  src/fal/utils.py
96
97
  src/fal/workflows.py
97
98
  src/fal/api/__init__.py
@@ -1,5 +1,5 @@
1
- isolate[build]<0.22.0,>=0.18.0
2
- isolate-proto>=0.29.2
1
+ isolate[build]<0.23.0,>=0.22.0
2
+ isolate-proto>=0.29.4
3
3
  grpcio<2,>=1.64.0
4
4
  dill==0.3.7
5
5
  cloudpickle==3.0.0
@@ -22,8 +22,8 @@ authors = [{ name = "Features & Labels <support@fal.ai>"}]
22
22
  readme = "README.md"
23
23
  requires-python = ">=3.8"
24
24
  dependencies = [
25
- "isolate[build]>=0.18.0,<0.22.0",
26
- "isolate-proto>=0.29.2",
25
+ "isolate[build]>=0.22.0,<0.23.0",
26
+ "isolate-proto>=0.29.4",
27
27
  "grpcio>=1.64.0,<2",
28
28
  "dill==0.3.7",
29
29
  "cloudpickle==3.0.0",
@@ -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.59.0'
32
- __version_tuple__ = version_tuple = (1, 59, 0)
31
+ __version__ = version = '1.61.0'
32
+ __version_tuple__ = version_tuple = (1, 61, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,16 @@
1
+ from .api import * # noqa: F403
2
+ from .client import ( # noqa: F401
3
+ AppsNamespace,
4
+ KeysNamespace,
5
+ RunnersNamespace,
6
+ SecretsNamespace,
7
+ SyncServerlessClient,
8
+ )
9
+
10
+ __all__ = [
11
+ "SyncServerlessClient",
12
+ "AppsNamespace",
13
+ "RunnersNamespace",
14
+ "KeysNamespace",
15
+ "SecretsNamespace",
16
+ ]
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import inspect
4
5
  import os
5
6
  import sys
@@ -450,6 +451,7 @@ class FalServerlessHost(Host):
450
451
  "app_files_ignore",
451
452
  "app_files_context_dir",
452
453
  "health_check_config",
454
+ "skip_retry_conditions",
453
455
  }
454
456
  )
455
457
 
@@ -548,6 +550,8 @@ class FalServerlessHost(Host):
548
550
  request_timeout = options.host.get("request_timeout")
549
551
  startup_timeout = options.host.get("startup_timeout")
550
552
  regions = options.host.get("regions")
553
+ health_check_config = options.host.get("health_check_config")
554
+ skip_retry_conditions = options.host.get("skip_retry_conditions")
551
555
  machine_requirements = MachineRequirements(
552
556
  machine_types=machine_type, # type: ignore
553
557
  num_gpus=options.host.get("num_gpus"),
@@ -567,8 +571,6 @@ class FalServerlessHost(Host):
567
571
  valid_regions=regions,
568
572
  )
569
573
 
570
- health_check_config = options.host.get("health_check_config")
571
-
572
574
  app_files = self._app_files_sync(options)
573
575
 
574
576
  partial_func = _prepare_partial_func(func)
@@ -596,6 +598,7 @@ class FalServerlessHost(Host):
596
598
  # By default, logs are public
597
599
  private_logs=options.host.get("private_logs", False),
598
600
  files=app_files,
601
+ skip_retry_conditions=skip_retry_conditions,
599
602
  ):
600
603
  for log in partial_result.logs:
601
604
  self._log_printer.print(log)
@@ -699,16 +702,20 @@ class FalServerlessHost(Host):
699
702
  from fal.console import console
700
703
 
701
704
  if service_urls := partial_result.service_urls:
702
- console.print("Playground:")
705
+ from fal.flags import URL_OUTPUT
706
+
703
707
  endpoints = getattr(func, "_routes", ["/"]) # type: ignore[attr-defined]
704
- for endpoint in endpoints:
705
- console.print(f"\t{service_urls.playground}{endpoint}")
706
- console.print("Synchronous Endpoints:")
707
- for endpoint in endpoints:
708
- console.print(f"\t{service_urls.run}{endpoint}")
709
- console.print("Asynchronous Endpoints (Recommended):")
710
- for endpoint in endpoints:
711
- console.print(f"\t{service_urls.queue}{endpoint}")
708
+ if URL_OUTPUT != "none":
709
+ console.print("Playground:")
710
+ for endpoint in endpoints:
711
+ console.print(f"\t{service_urls.playground}{endpoint}")
712
+ if URL_OUTPUT == "all":
713
+ console.print("Synchronous Endpoints:")
714
+ for endpoint in endpoints:
715
+ console.print(f"\t{service_urls.run}{endpoint}")
716
+ console.print("Asynchronous Endpoints (Recommended):")
717
+ for endpoint in endpoints:
718
+ console.print(f"\t{service_urls.queue}{endpoint}")
712
719
 
713
720
  for log in partial_result.logs:
714
721
  if (
@@ -801,6 +808,7 @@ def function(
801
808
  exposed_port: int | None = None,
802
809
  max_concurrency: int | None = None,
803
810
  local_python_modules: list[str] | None = None,
811
+ force_env_build: bool = False,
804
812
  ) -> Callable[
805
813
  [Callable[Concatenate[ArgsT], ReturnT]], IsolatedFunction[ArgsT, ReturnT]
806
814
  ]: ...
@@ -818,6 +826,7 @@ def function(
818
826
  exposed_port: int | None = None,
819
827
  max_concurrency: int | None = None,
820
828
  local_python_modules: list[str] | None = None,
829
+ force_env_build: bool = False,
821
830
  ) -> Callable[
822
831
  [Callable[Concatenate[ArgsT], ReturnT]], ServedIsolatedFunction[ArgsT, ReturnT]
823
832
  ]: ...
@@ -850,6 +859,7 @@ def function(
850
859
  request_timeout: int | None = None,
851
860
  startup_timeout: int | None = None,
852
861
  setup_function: Callable[..., None] | None = None,
862
+ force_env_build: bool = False,
853
863
  _base_image: str | None = None,
854
864
  _scheduler: str | None = None,
855
865
  ) -> Callable[
@@ -883,6 +893,7 @@ def function(
883
893
  request_timeout: int | None = None,
884
894
  startup_timeout: int | None = None,
885
895
  setup_function: Callable[..., None] | None = None,
896
+ force_env_build: bool = False,
886
897
  _base_image: str | None = None,
887
898
  _scheduler: str | None = None,
888
899
  ) -> Callable[
@@ -968,6 +979,7 @@ def function(
968
979
  request_timeout: int | None = None,
969
980
  startup_timeout: int | None = None,
970
981
  setup_function: Callable[..., None] | None = None,
982
+ force_env_build: bool = False,
971
983
  _base_image: str | None = None,
972
984
  _scheduler: str | None = None,
973
985
  ) -> Callable[
@@ -1006,6 +1018,7 @@ def function(
1006
1018
  request_timeout: int | None = None,
1007
1019
  startup_timeout: int | None = None,
1008
1020
  setup_function: Callable[..., None] | None = None,
1021
+ force_env_build: bool = False,
1009
1022
  _base_image: str | None = None,
1010
1023
  _scheduler: str | None = None,
1011
1024
  ) -> Callable[
@@ -1038,6 +1051,7 @@ def function(
1038
1051
  request_timeout: int | None = None,
1039
1052
  startup_timeout: int | None = None,
1040
1053
  setup_function: Callable[..., None] | None = None,
1054
+ force_env_build: bool = False,
1041
1055
  _base_image: str | None = None,
1042
1056
  _scheduler: str | None = None,
1043
1057
  ) -> Callable[
@@ -1070,6 +1084,7 @@ def function(
1070
1084
  request_timeout: int | None = None,
1071
1085
  startup_timeout: int | None = None,
1072
1086
  setup_function: Callable[..., None] | None = None,
1087
+ force_env_build: bool = False,
1073
1088
  _base_image: str | None = None,
1074
1089
  _scheduler: str | None = None,
1075
1090
  ) -> Callable[
@@ -1095,6 +1110,10 @@ def function( # type: ignore
1095
1110
  if kind == "container" and config.get("app_files"):
1096
1111
  raise ValueError("app_files is not supported for container apps.")
1097
1112
 
1113
+ if config.get("force_env_build") is not None:
1114
+ force_env_build = config.pop("force_env_build")
1115
+ config["force"] = force_env_build
1116
+
1098
1117
  options = host.parse_options(kind=kind, **config)
1099
1118
 
1100
1119
  def wrapper(func: Callable[ArgsT, ReturnT]):
@@ -1235,8 +1254,16 @@ class BaseServable:
1235
1254
  # This is supposed to make it easier to understand to the user
1236
1255
  # that the error comes from the app and not our platform.
1237
1256
  if exc.detail == "Not Found":
1257
+ # For 404 errors (non-existent endpoints), set billable units to 0.
1258
+ # This prevents users from being charged when they hit endpoints that
1259
+ # don't exist. Without this, the platform would use the default billable
1260
+ # units for the endpoint, incorrectly charging users for failed requests
1261
+ headers = dict(exc.headers) if exc.headers else {}
1262
+ headers["x-fal-billable-units"] = "0"
1238
1263
  return JSONResponse(
1239
- {"detail": f"Path {request.url.path} not found"}, 404
1264
+ {"detail": f"Path {request.url.path} not found"},
1265
+ 404,
1266
+ headers=headers,
1240
1267
  )
1241
1268
  else:
1242
1269
  # If it's not a generic 404, just return the original message.
@@ -1248,7 +1275,17 @@ class BaseServable:
1248
1275
 
1249
1276
  @_app.exception_handler(FieldException)
1250
1277
  async def field_exception_handler(request: Request, exc: FieldException):
1251
- return JSONResponse(exc.to_pydantic_format(), exc.status_code)
1278
+ headers = {}
1279
+ if exc.billable_units:
1280
+ # poor man's validation. we dont want people to pass in
1281
+ # non-numeric values.
1282
+ units_float = float(exc.billable_units)
1283
+ # we dont want to add 8 decimal places for ints.
1284
+ format_string = ".0f" if isinstance(exc.billable_units, int) else ".8f"
1285
+ headers["x-fal-billable-units"] = format(units_float, format_string)
1286
+ return JSONResponse(
1287
+ exc.to_pydantic_format(), exc.status_code, headers=headers
1288
+ )
1252
1289
 
1253
1290
  # ref: https://github.com/fastapi/fastapi/blob/37c8e7d76b4b47eb2c4cced6b4de59eb3d5f08eb/fastapi/exception_handlers.py#L20
1254
1291
  @_app.exception_handler(RequestValidationError)
@@ -1331,12 +1368,9 @@ class BaseServable:
1331
1368
  "Failed to generate OpenAPI metadata for function"
1332
1369
  ) from e
1333
1370
 
1334
- def serve(self) -> None:
1335
- import asyncio
1336
-
1371
+ async def serve(self) -> None:
1337
1372
  from prometheus_client import Gauge
1338
1373
  from starlette_exporter import handle_metrics
1339
- from uvicorn import Config
1340
1374
 
1341
1375
  # NOTE: this uses the global prometheus registry
1342
1376
  app_info = Gauge("fal_app_info", "Fal application information", ["version"])
@@ -1346,40 +1380,32 @@ class BaseServable:
1346
1380
 
1347
1381
  # We use the default workers=1 config because setup function can be heavy
1348
1382
  # and it runs once per worker.
1349
- server = Server(
1350
- config=Config(app, host="0.0.0.0", port=8080, timeout_keep_alive=300)
1383
+ server = uvicorn.Server(
1384
+ config=uvicorn.Config(
1385
+ app, host="0.0.0.0", port=8080, timeout_keep_alive=300, lifespan="on"
1386
+ )
1351
1387
  )
1352
1388
  metrics_app = FastAPI()
1353
1389
  metrics_app.add_route("/metrics", handle_metrics)
1354
- metrics_server = Server(config=Config(metrics_app, host="0.0.0.0", port=9090))
1390
+ metrics_server = uvicorn.Server(
1391
+ config=uvicorn.Config(metrics_app, host="0.0.0.0", port=9090)
1392
+ )
1355
1393
 
1356
1394
  async def _serve() -> None:
1357
1395
  tasks = {
1358
- asyncio.create_task(server.serve()): server,
1359
- asyncio.create_task(metrics_server.serve()): metrics_server,
1396
+ asyncio.create_task(server.serve()),
1397
+ asyncio.create_task(metrics_server.serve()),
1360
1398
  }
1361
1399
 
1362
- _, pending = await asyncio.wait(
1363
- tasks.keys(),
1364
- return_when=asyncio.FIRST_COMPLETED,
1400
+ await asyncio.wait(
1401
+ tasks,
1402
+ return_when=asyncio.ALL_COMPLETED,
1365
1403
  )
1366
- if not pending:
1367
- return
1368
-
1369
- # try graceful shutdown
1370
- for task in pending:
1371
- tasks[task].should_exit = True
1372
- _, pending = await asyncio.wait(pending, timeout=2)
1373
- if not pending:
1374
- return
1404
+ # we do not take care of pending tasks here.
1405
+ # each task should be responsible for its own cleanup.
1406
+ # graceful termination and timeout should be handled by external scheduler.
1375
1407
 
1376
- for task in pending:
1377
- task.cancel()
1378
- await asyncio.wait(pending)
1379
-
1380
- with suppress(asyncio.CancelledError):
1381
- asyncio.set_event_loop(asyncio.new_event_loop())
1382
- asyncio.run(_serve())
1408
+ await _serve()
1383
1409
 
1384
1410
 
1385
1411
  class ServeWrapper(BaseServable):
@@ -1393,13 +1419,13 @@ class ServeWrapper(BaseServable):
1393
1419
  RouteSignature("/"): self._func,
1394
1420
  }
1395
1421
 
1396
- def __call__(self, *args, **kwargs) -> None:
1422
+ async def __call__(self, *args, **kwargs) -> None:
1397
1423
  if len(args) != 0 or len(kwargs) != 0:
1398
1424
  print(
1399
1425
  f"[warning] {self._func.__name__} function is served with no arguments."
1400
1426
  )
1401
1427
 
1402
- self.serve()
1428
+ await self.serve()
1403
1429
 
1404
1430
 
1405
1431
  @dataclass
@@ -1519,7 +1545,7 @@ class IsolatedFunction(Generic[ArgsT, ReturnT]):
1519
1545
  if serve_mode:
1520
1546
  # This type can be safely ignored because this case only happens when it
1521
1547
  # is a ServedIsolatedFunction
1522
- serve_func: Callable[[], None] = ServeWrapper(self.raw_func)
1548
+ serve_func = ServeWrapper(self.raw_func)
1523
1549
  return serve_func # type: ignore
1524
1550
  else:
1525
1551
  return self.raw_func
@@ -1545,15 +1571,3 @@ class ServedIsolatedFunction(
1545
1571
  def on(
1546
1572
  self, host: Host | None = None, *, serve: Literal[False], **config: Any
1547
1573
  ) -> IsolatedFunction[ArgsT, ReturnT]: ...
1548
-
1549
-
1550
- class Server(uvicorn.Server):
1551
- """Server is a uvicorn.Server that actually plays nicely with signals.
1552
- By default, uvicorn's Server class overwrites the signal handler for SIGINT,
1553
- swallowing the signal and preventing other tasks from cancelling.
1554
- This class allows the task to be gracefully cancelled using asyncio's built-in task
1555
- cancellation or with an event, like aiohttp.
1556
- """
1557
-
1558
- def install_signal_handlers(self) -> None:
1559
- pass