fal 1.37.0__tar.gz → 1.39.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 (187) hide show
  1. {fal-1.37.0/fal.egg-info → fal-1.39.0}/PKG-INFO +6 -4
  2. {fal-1.37.0 → fal-1.39.0/fal.egg-info}/PKG-INFO +6 -4
  3. {fal-1.37.0 → fal-1.39.0}/fal.egg-info/requires.txt +5 -3
  4. {fal-1.37.0 → fal-1.39.0}/pyproject.toml +5 -3
  5. {fal-1.37.0 → fal-1.39.0}/src/fal/_fal_version.py +2 -2
  6. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/apps.py +27 -2
  7. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/parser.py +57 -0
  8. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/runners.py +41 -5
  9. {fal-1.37.0 → fal-1.39.0}/src/fal/sdk.py +23 -4
  10. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/image/__init__.py +6 -5
  11. {fal-1.37.0 → fal-1.39.0}/tests/test_stability.py +9 -7
  12. {fal-1.37.0 → fal-1.39.0}/.gitignore +0 -0
  13. {fal-1.37.0 → fal-1.39.0}/Makefile +0 -0
  14. {fal-1.37.0 → fal-1.39.0}/README.md +0 -0
  15. {fal-1.37.0 → fal-1.39.0}/docs/conf.py +0 -0
  16. {fal-1.37.0 → fal-1.39.0}/docs/index.rst +0 -0
  17. {fal-1.37.0 → fal-1.39.0}/fal.egg-info/SOURCES.txt +0 -0
  18. {fal-1.37.0 → fal-1.39.0}/fal.egg-info/dependency_links.txt +0 -0
  19. {fal-1.37.0 → fal-1.39.0}/fal.egg-info/entry_points.txt +0 -0
  20. {fal-1.37.0 → fal-1.39.0}/fal.egg-info/top_level.txt +0 -0
  21. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/README.md +0 -0
  22. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  23. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  24. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  25. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  26. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  27. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  28. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  29. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  30. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  31. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  32. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  33. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  34. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  35. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  36. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  37. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  38. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  39. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  40. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  41. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  42. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  43. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  44. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  45. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  46. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  47. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  48. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  49. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  50. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  51. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  52. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  53. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  54. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  55. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  56. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  57. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  58. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  59. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  60. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  61. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  62. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  63. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  64. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  65. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  66. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  67. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  68. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  69. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  70. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  71. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  72. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  73. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  74. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  75. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  76. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  77. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  78. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  79. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  80. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  81. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  82. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  83. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  84. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  85. {fal-1.37.0 → fal-1.39.0}/openapi-fal-rest/pyproject.toml +0 -0
  86. {fal-1.37.0 → fal-1.39.0}/openapi_rest.config.yaml +0 -0
  87. {fal-1.37.0 → fal-1.39.0}/setup.cfg +0 -0
  88. {fal-1.37.0 → fal-1.39.0}/src/fal/__init__.py +0 -0
  89. {fal-1.37.0 → fal-1.39.0}/src/fal/__main__.py +0 -0
  90. {fal-1.37.0 → fal-1.39.0}/src/fal/_serialization.py +0 -0
  91. {fal-1.37.0 → fal-1.39.0}/src/fal/_version.py +0 -0
  92. {fal-1.37.0 → fal-1.39.0}/src/fal/api.py +0 -0
  93. {fal-1.37.0 → fal-1.39.0}/src/fal/app.py +0 -0
  94. {fal-1.37.0 → fal-1.39.0}/src/fal/apps.py +0 -0
  95. {fal-1.37.0 → fal-1.39.0}/src/fal/auth/__init__.py +0 -0
  96. {fal-1.37.0 → fal-1.39.0}/src/fal/auth/auth0.py +0 -0
  97. {fal-1.37.0 → fal-1.39.0}/src/fal/auth/local.py +0 -0
  98. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/__init__.py +0 -0
  99. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/_utils.py +0 -0
  100. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/api.py +0 -0
  101. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/auth.py +0 -0
  102. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/cli_nested_json.py +0 -0
  103. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/create.py +0 -0
  104. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/debug.py +0 -0
  105. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/deploy.py +0 -0
  106. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/doctor.py +0 -0
  107. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/files.py +0 -0
  108. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/keys.py +0 -0
  109. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/main.py +0 -0
  110. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/profile.py +0 -0
  111. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/run.py +0 -0
  112. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/secrets.py +0 -0
  113. {fal-1.37.0 → fal-1.39.0}/src/fal/cli/teams.py +0 -0
  114. {fal-1.37.0 → fal-1.39.0}/src/fal/config.py +0 -0
  115. {fal-1.37.0 → fal-1.39.0}/src/fal/console/__init__.py +0 -0
  116. {fal-1.37.0 → fal-1.39.0}/src/fal/console/icons.py +0 -0
  117. {fal-1.37.0 → fal-1.39.0}/src/fal/console/ux.py +0 -0
  118. {fal-1.37.0 → fal-1.39.0}/src/fal/container.py +0 -0
  119. {fal-1.37.0 → fal-1.39.0}/src/fal/exceptions/__init__.py +0 -0
  120. {fal-1.37.0 → fal-1.39.0}/src/fal/exceptions/_base.py +0 -0
  121. {fal-1.37.0 → fal-1.39.0}/src/fal/exceptions/_cuda.py +0 -0
  122. {fal-1.37.0 → fal-1.39.0}/src/fal/exceptions/auth.py +0 -0
  123. {fal-1.37.0 → fal-1.39.0}/src/fal/files.py +0 -0
  124. {fal-1.37.0 → fal-1.39.0}/src/fal/flags.py +0 -0
  125. {fal-1.37.0 → fal-1.39.0}/src/fal/logging/__init__.py +0 -0
  126. {fal-1.37.0 → fal-1.39.0}/src/fal/logging/isolate.py +0 -0
  127. {fal-1.37.0 → fal-1.39.0}/src/fal/logging/style.py +0 -0
  128. {fal-1.37.0 → fal-1.39.0}/src/fal/logging/trace.py +0 -0
  129. {fal-1.37.0 → fal-1.39.0}/src/fal/logging/user.py +0 -0
  130. {fal-1.37.0 → fal-1.39.0}/src/fal/project.py +0 -0
  131. {fal-1.37.0 → fal-1.39.0}/src/fal/py.typed +0 -0
  132. {fal-1.37.0 → fal-1.39.0}/src/fal/rest_client.py +0 -0
  133. {fal-1.37.0 → fal-1.39.0}/src/fal/sync.py +0 -0
  134. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/__init__.py +0 -0
  135. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/audio/__init__.py +0 -0
  136. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/audio/audio.py +0 -0
  137. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/exceptions.py +0 -0
  138. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/file/__init__.py +0 -0
  139. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/file/file.py +0 -0
  140. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/file/providers/fal.py +0 -0
  141. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/file/providers/gcp.py +0 -0
  142. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/file/providers/r2.py +0 -0
  143. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/file/providers/s3.py +0 -0
  144. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/file/types.py +0 -0
  145. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/image/image.py +0 -0
  146. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  147. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  148. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  149. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  150. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  151. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/image/safety_checker.py +0 -0
  152. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/kv.py +0 -0
  153. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/optimize.py +0 -0
  154. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/types.py +0 -0
  155. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/utils/__init__.py +0 -0
  156. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/utils/download_utils.py +0 -0
  157. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/utils/endpoint.py +0 -0
  158. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/utils/retry.py +0 -0
  159. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/utils/setup_utils.py +0 -0
  160. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/video/__init__.py +0 -0
  161. {fal-1.37.0 → fal-1.39.0}/src/fal/toolkit/video/video.py +0 -0
  162. {fal-1.37.0 → fal-1.39.0}/src/fal/utils.py +0 -0
  163. {fal-1.37.0 → fal-1.39.0}/src/fal/workflows.py +0 -0
  164. {fal-1.37.0 → fal-1.39.0}/tests/__init__.py +0 -0
  165. {fal-1.37.0 → fal-1.39.0}/tests/assets/cat.png +0 -0
  166. {fal-1.37.0 → fal-1.39.0}/tests/cli/__init__.py +0 -0
  167. {fal-1.37.0 → fal-1.39.0}/tests/cli/test_apps.py +0 -0
  168. {fal-1.37.0 → fal-1.39.0}/tests/cli/test_auth.py +0 -0
  169. {fal-1.37.0 → fal-1.39.0}/tests/cli/test_deploy.py +0 -0
  170. {fal-1.37.0 → fal-1.39.0}/tests/cli/test_keys.py +0 -0
  171. {fal-1.37.0 → fal-1.39.0}/tests/cli/test_run.py +0 -0
  172. {fal-1.37.0 → fal-1.39.0}/tests/cli/test_secrets.py +0 -0
  173. {fal-1.37.0 → fal-1.39.0}/tests/conftest.py +0 -0
  174. {fal-1.37.0 → fal-1.39.0}/tests/integration_test.py +0 -0
  175. {fal-1.37.0 → fal-1.39.0}/tests/mainify_package/__init__.py +0 -0
  176. {fal-1.37.0 → fal-1.39.0}/tests/mainify_package/impl.py +0 -0
  177. {fal-1.37.0 → fal-1.39.0}/tests/mainify_package/utils.py +0 -0
  178. {fal-1.37.0 → fal-1.39.0}/tests/mainify_target.py +0 -0
  179. {fal-1.37.0 → fal-1.39.0}/tests/test_apps.py +0 -0
  180. {fal-1.37.0 → fal-1.39.0}/tests/test_files.py +0 -0
  181. {fal-1.37.0 → fal-1.39.0}/tests/test_kv.py +0 -0
  182. {fal-1.37.0 → fal-1.39.0}/tests/toolkit/file/providers/test_fal_retry.py +0 -0
  183. {fal-1.37.0 → fal-1.39.0}/tests/toolkit/file_test.py +0 -0
  184. {fal-1.37.0 → fal-1.39.0}/tests/toolkit/image_test.py +0 -0
  185. {fal-1.37.0 → fal-1.39.0}/tests/toolkit/test_types.py +0 -0
  186. {fal-1.37.0 → fal-1.39.0}/tests/toolkit/utils/retry.py +0 -0
  187. {fal-1.37.0 → fal-1.39.0}/tools/demo_script.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.37.0
3
+ Version: 1.39.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.20.0,>=0.18.0
9
- Requires-Dist: isolate-proto<0.15.0,>=0.14.1
9
+ Requires-Dist: isolate-proto<0.17.0,>=0.16.0
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
@@ -17,7 +17,7 @@ Requires-Dist: opentelemetry-sdk<2,>=1.15.0
17
17
  Requires-Dist: grpc-interceptor<1,>=0.15.0
18
18
  Requires-Dist: colorama<1,>=0.4.6
19
19
  Requires-Dist: portalocker<3,>=2.7.0
20
- Requires-Dist: rich<14,>=13.3.2
20
+ Requires-Dist: rich<15,>=13.3.2
21
21
  Requires-Dist: rich_argparse
22
22
  Requires-Dist: packaging>=21.3
23
23
  Requires-Dist: pathspec<1,>=0.11.1
@@ -28,10 +28,12 @@ Requires-Dist: httpx>=0.15.4
28
28
  Requires-Dist: attrs>=21.3.0
29
29
  Requires-Dist: python-dateutil<3,>=2.8.0
30
30
  Requires-Dist: types-python-dateutil<3,>=2.8.0
31
+ Requires-Dist: dateparser<2,>=1.2.0
32
+ Requires-Dist: types-dateparser<2,>=1.2.0
31
33
  Requires-Dist: importlib-metadata>=4.4; python_version < "3.10"
32
34
  Requires-Dist: msgpack<2,>=1.0.7
33
35
  Requires-Dist: websockets>=12.0
34
- Requires-Dist: pillow<11,>=10.2.0
36
+ Requires-Dist: pillow<12,>=10.2.0
35
37
  Requires-Dist: pyjwt[crypto]<3,>=2.8.0
36
38
  Requires-Dist: uvicorn<1,>=0.29.0
37
39
  Requires-Dist: cookiecutter
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.37.0
3
+ Version: 1.39.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.20.0,>=0.18.0
9
- Requires-Dist: isolate-proto<0.15.0,>=0.14.1
9
+ Requires-Dist: isolate-proto<0.17.0,>=0.16.0
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
@@ -17,7 +17,7 @@ Requires-Dist: opentelemetry-sdk<2,>=1.15.0
17
17
  Requires-Dist: grpc-interceptor<1,>=0.15.0
18
18
  Requires-Dist: colorama<1,>=0.4.6
19
19
  Requires-Dist: portalocker<3,>=2.7.0
20
- Requires-Dist: rich<14,>=13.3.2
20
+ Requires-Dist: rich<15,>=13.3.2
21
21
  Requires-Dist: rich_argparse
22
22
  Requires-Dist: packaging>=21.3
23
23
  Requires-Dist: pathspec<1,>=0.11.1
@@ -28,10 +28,12 @@ Requires-Dist: httpx>=0.15.4
28
28
  Requires-Dist: attrs>=21.3.0
29
29
  Requires-Dist: python-dateutil<3,>=2.8.0
30
30
  Requires-Dist: types-python-dateutil<3,>=2.8.0
31
+ Requires-Dist: dateparser<2,>=1.2.0
32
+ Requires-Dist: types-dateparser<2,>=1.2.0
31
33
  Requires-Dist: importlib-metadata>=4.4; python_version < "3.10"
32
34
  Requires-Dist: msgpack<2,>=1.0.7
33
35
  Requires-Dist: websockets>=12.0
34
- Requires-Dist: pillow<11,>=10.2.0
36
+ Requires-Dist: pillow<12,>=10.2.0
35
37
  Requires-Dist: pyjwt[crypto]<3,>=2.8.0
36
38
  Requires-Dist: uvicorn<1,>=0.29.0
37
39
  Requires-Dist: cookiecutter
@@ -1,5 +1,5 @@
1
1
  isolate[build]<0.20.0,>=0.18.0
2
- isolate-proto<0.15.0,>=0.14.1
2
+ isolate-proto<0.17.0,>=0.16.0
3
3
  grpcio<2,>=1.64.0
4
4
  dill==0.3.7
5
5
  cloudpickle==3.0.0
@@ -10,7 +10,7 @@ opentelemetry-sdk<2,>=1.15.0
10
10
  grpc-interceptor<1,>=0.15.0
11
11
  colorama<1,>=0.4.6
12
12
  portalocker<3,>=2.7.0
13
- rich<14,>=13.3.2
13
+ rich<15,>=13.3.2
14
14
  rich_argparse
15
15
  packaging>=21.3
16
16
  pathspec<1,>=0.11.1
@@ -21,9 +21,11 @@ httpx>=0.15.4
21
21
  attrs>=21.3.0
22
22
  python-dateutil<3,>=2.8.0
23
23
  types-python-dateutil<3,>=2.8.0
24
+ dateparser<2,>=1.2.0
25
+ types-dateparser<2,>=1.2.0
24
26
  msgpack<2,>=1.0.7
25
27
  websockets>=12.0
26
- pillow<11,>=10.2.0
28
+ pillow<12,>=10.2.0
27
29
  pyjwt[crypto]<3,>=2.8.0
28
30
  uvicorn<1,>=0.29.0
29
31
  cookiecutter
@@ -23,7 +23,7 @@ readme = "README.md"
23
23
  requires-python = ">=3.8"
24
24
  dependencies = [
25
25
  "isolate[build]>=0.18.0,<0.20.0",
26
- "isolate-proto>=0.14.1,<0.15.0",
26
+ "isolate-proto>=0.16.0,<0.17.0",
27
27
  "grpcio>=1.64.0,<2",
28
28
  "dill==0.3.7",
29
29
  "cloudpickle==3.0.0",
@@ -34,7 +34,7 @@ dependencies = [
34
34
  "grpc-interceptor>=0.15.0,<1",
35
35
  "colorama>=0.4.6,<1",
36
36
  "portalocker>=2.7.0,<3",
37
- "rich>=13.3.2,<14",
37
+ "rich>=13.3.2,<15",
38
38
  "rich_argparse",
39
39
  "packaging>=21.3",
40
40
  "pathspec>=0.11.1,<1",
@@ -47,11 +47,13 @@ dependencies = [
47
47
  "attrs>=21.3.0",
48
48
  "python-dateutil>=2.8.0,<3",
49
49
  "types-python-dateutil>=2.8.0,<3",
50
+ "dateparser>=1.2.0,<2",
51
+ "types-dateparser>=1.2.0,<2",
50
52
  # For 3.9 and earlier, importlib-metadata's newer versions are included in the standard library.
51
53
  'importlib-metadata>=4.4; python_version < "3.10"',
52
54
  "msgpack>=1.0.7,<2",
53
55
  "websockets>=12.0",
54
- "pillow>=10.2.0,<11",
56
+ "pillow>=10.2.0,<12",
55
57
  "pyjwt[crypto]>=2.8.0,<3",
56
58
  "uvicorn>=0.29.0,<1",
57
59
  "cookiecutter",
@@ -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.37.0'
32
- __version_tuple__ = version_tuple = (1, 37, 0)
31
+ __version__ = version = '1.39.0'
32
+ __version_tuple__ = version_tuple = (1, 39, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -8,7 +8,7 @@ import fal.cli.runners as runners
8
8
  from fal.sdk import RunnerState
9
9
 
10
10
  from ._utils import get_client
11
- from .parser import FalClientParser, get_output_parser
11
+ from .parser import FalClientParser, SinceAction, get_output_parser
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from fal.sdk import AliasInfo, ApplicationInfo
@@ -291,7 +291,14 @@ def _add_set_rev_parser(subparsers, parents):
291
291
  def _runners(args):
292
292
  client = get_client(args.host, args.team)
293
293
  with client.connect() as connection:
294
- alias_runners = connection.list_alias_runners(alias=args.app_name)
294
+ start_time = getattr(args, "since", None)
295
+ alias_runners = connection.list_alias_runners(
296
+ alias=args.app_name, start_time=start_time
297
+ )
298
+ if getattr(args, "state", None):
299
+ states = set(args.state)
300
+ if "all" not in states:
301
+ alias_runners = [r for r in alias_runners if r.state.value in states]
295
302
  if args.output == "pretty":
296
303
  runners_table = runners.runners_table(alias_runners)
297
304
  pending_runners = [
@@ -330,6 +337,24 @@ def _add_runners_parser(subparsers, parents):
330
337
  "app_name",
331
338
  help="Application name.",
332
339
  )
340
+ parser.add_argument(
341
+ "--since",
342
+ default=None,
343
+ action=SinceAction,
344
+ limit="1 day",
345
+ help=(
346
+ "Show dead runners since the given time. "
347
+ "Accepts 'now', relative like '30m', '1h', '1d', "
348
+ "or an ISO timestamp. Max 24 hours."
349
+ ),
350
+ )
351
+ parser.add_argument(
352
+ "--state",
353
+ choices=["all", "running", "pending", "setup", "dead"],
354
+ nargs="+",
355
+ default=None,
356
+ help=("Filter by runner state(s). Choose one or more, or 'all'(default)."),
357
+ )
333
358
  parser.set_defaults(func=_runners)
334
359
 
335
360
 
@@ -1,5 +1,7 @@
1
1
  import argparse
2
2
  import sys
3
+ from datetime import datetime, timedelta
4
+ from typing import Optional
3
5
 
4
6
  import rich_argparse
5
7
 
@@ -55,6 +57,61 @@ class DictAction(argparse.Action):
55
57
  setattr(args, self.dest, d)
56
58
 
57
59
 
60
+ class SinceAction(argparse.Action):
61
+ LIMIT_LEEWAY = timedelta(minutes=1)
62
+
63
+ def _parse_since(self, value: str) -> Optional[datetime]:
64
+ import dateparser
65
+
66
+ return dateparser.parse(
67
+ value,
68
+ settings={
69
+ "PREFER_DATES_FROM": "past",
70
+ },
71
+ )
72
+
73
+ def __init__(self, *args, **kwargs):
74
+ self._limit = kwargs.pop("limit", None)
75
+ if self._limit:
76
+ if not isinstance(self._limit, str):
77
+ raise ValueError(
78
+ f"Invalid 'limit' value for SinceAction: {self._limit!r}"
79
+ )
80
+
81
+ self._limit_dt = self._parse_since(self._limit)
82
+ if not self._limit_dt:
83
+ raise ValueError(
84
+ f"Invalid 'limit' value for SinceAction: {self._limit!r}"
85
+ )
86
+
87
+ super().__init__(*args, **kwargs)
88
+
89
+ def __call__(self, parser, args, values, option_string=None): # noqa: ARG002
90
+ if values is None:
91
+ setattr(args, self.dest, None)
92
+ return
93
+
94
+ dt = self._parse_since(values)
95
+ if not dt:
96
+ raise argparse.ArgumentError(
97
+ self,
98
+ (
99
+ f"Invalid since value: {values}. "
100
+ "Use 'now', relative like '15m' or '24h ago', "
101
+ "or an ISO timestamp."
102
+ ),
103
+ )
104
+
105
+ if self._limit_dt is not None:
106
+ if dt < self._limit_dt - self.LIMIT_LEEWAY:
107
+ raise argparse.ArgumentError(
108
+ self,
109
+ f"Since value is older than the allowed limit {self._limit}.",
110
+ )
111
+
112
+ setattr(args, self.dest, dt)
113
+
114
+
58
115
  def _find_parser(parser, func):
59
116
  defaults = parser._defaults
60
117
  if not func or func == defaults.get("func"):
@@ -1,12 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
+ from datetime import timedelta
4
5
  from typing import List
5
6
 
6
7
  from fal.sdk import RunnerInfo, RunnerState
7
8
 
8
9
  from ._utils import get_client
9
- from .parser import FalClientParser, get_output_parser
10
+ from .parser import FalClientParser, SinceAction, get_output_parser
10
11
 
11
12
 
12
13
  def runners_table(runners: List[RunnerInfo]):
@@ -42,17 +43,20 @@ def runners_table(runners: List[RunnerInfo]):
42
43
  # consistent
43
44
  in_flight = f"{in_flight} [dim]({missing_leases})[/]"
44
45
 
46
+ uptime = timedelta(
47
+ seconds=int(runner.uptime.total_seconds()),
48
+ )
45
49
  table.add_row(
46
50
  runner.alias,
47
51
  # Mark lost runners in red
48
52
  runner.runner_id if present else f"[red]{runner.runner_id}[/]",
49
53
  in_flight,
50
54
  (
51
- "N/A (active)"
55
+ "N/A"
52
56
  if runner.expiration_countdown is None
53
57
  else f"{runner.expiration_countdown}s"
54
58
  ),
55
- f"{runner.uptime} ({runner.uptime.total_seconds()}s)",
59
+ f"{uptime} ({uptime.total_seconds():.0f}s)",
56
60
  runner.revision,
57
61
  runner.state.value,
58
62
  )
@@ -111,17 +115,31 @@ def _list_json(args, runners: list[RunnerInfo]):
111
115
  def _list(args):
112
116
  client = get_client(args.host, args.team)
113
117
  with client.connect() as connection:
114
- runners = connection.list_runners()
118
+ start_time = getattr(args, "since", None)
119
+ runners = connection.list_runners(start_time=start_time)
120
+
121
+ if getattr(args, "state", None):
122
+ states = set(args.state)
123
+ if "all" not in states:
124
+ runners = [r for r in runners if r.state.value in states]
115
125
  pending_runners = [
116
126
  runner for runner in runners if runner.state == RunnerState.PENDING
117
127
  ]
118
128
  setup_runners = [
119
129
  runner for runner in runners if runner.state == RunnerState.SETUP
120
130
  ]
131
+ dead_runners = [
132
+ runner for runner in runners if runner.state == RunnerState.DEAD
133
+ ]
121
134
  if args.output == "pretty":
122
135
  args.console.print(
123
136
  "Runners: "
124
- + str(len(runners) - len(pending_runners) - len(setup_runners))
137
+ + str(
138
+ len(runners)
139
+ - len(pending_runners)
140
+ - len(setup_runners)
141
+ - len(dead_runners)
142
+ )
125
143
  )
126
144
  args.console.print(f"Runners Pending: {len(pending_runners)}")
127
145
  args.console.print(f"Runners Setting Up: {len(setup_runners)}")
@@ -159,6 +177,24 @@ def _add_list_parser(subparsers, parents):
159
177
  help=list_help,
160
178
  parents=[*parents, get_output_parser()],
161
179
  )
180
+ parser.add_argument(
181
+ "--since",
182
+ default=None,
183
+ action=SinceAction,
184
+ limit="1 day",
185
+ help=(
186
+ "Show dead runners since the given time. "
187
+ "Accepts 'now', relative like '30m', '1h', '1d', "
188
+ "or an ISO timestamp. Max 24 hours."
189
+ ),
190
+ )
191
+ parser.add_argument(
192
+ "--state",
193
+ choices=["all", "running", "pending", "setup", "dead"],
194
+ nargs="+",
195
+ default=None,
196
+ help=("Filter by runner state(s). Choose one or more, or 'all'(default)."),
197
+ )
162
198
  parser.set_defaults(func=_list)
163
199
 
164
200
 
@@ -265,6 +265,7 @@ class RunnerState(Enum):
265
265
  RUNNING = "running"
266
266
  PENDING = "pending"
267
267
  SETUP = "setup"
268
+ DEAD = "dead"
268
269
  UNKNOWN = "unknown"
269
270
 
270
271
  @staticmethod
@@ -275,6 +276,8 @@ class RunnerState(Enum):
275
276
  return RunnerState.PENDING
276
277
  elif proto is isolate_proto.RunnerInfo.State.SETUP:
277
278
  return RunnerState.SETUP
279
+ elif proto is isolate_proto.RunnerInfo.State.DEAD:
280
+ return RunnerState.DEAD
278
281
  else:
279
282
  return RunnerState.UNKNOWN
280
283
 
@@ -811,8 +814,18 @@ class FalServerlessConnection:
811
814
  response: isolate_proto.ListAliasesResult = self.stub.ListAliases(request)
812
815
  return [from_grpc(alias) for alias in response.aliases]
813
816
 
814
- def list_alias_runners(self, alias: str) -> list[RunnerInfo]:
815
- request = isolate_proto.ListAliasRunnersRequest(alias=alias, list_pending=True)
817
+ def list_alias_runners(
818
+ self,
819
+ alias: str,
820
+ *,
821
+ list_pending: bool = True,
822
+ start_time: datetime | None = None,
823
+ ) -> list[RunnerInfo]:
824
+ kwargs = {"alias": alias, "list_pending": list_pending}
825
+ if start_time:
826
+ kwargs["start_time"] = isolate_proto.timestamp_from_datetime(start_time)
827
+
828
+ request = isolate_proto.ListAliasRunnersRequest(**kwargs)
816
829
  response = self.stub.ListAliasRunners(request)
817
830
  return [from_grpc(runner) for runner in response.runners]
818
831
 
@@ -839,7 +852,13 @@ class FalServerlessConnection:
839
852
  request = isolate_proto.KillRunnerRequest(runner_id=runner_id)
840
853
  self.stub.KillRunner(request)
841
854
 
842
- def list_runners(self) -> list[RunnerInfo]:
843
- request = isolate_proto.ListRunnersRequest(list_pending=True)
855
+ def list_runners(self, start_time: datetime | None = None) -> list[RunnerInfo]:
856
+ kwargs = {
857
+ "list_pending": True,
858
+ }
859
+ if start_time:
860
+ kwargs["start_time"] = isolate_proto.timestamp_from_datetime(start_time)
861
+
862
+ request = isolate_proto.ListRunnersRequest(**kwargs)
844
863
  response = self.stub.ListRunners(request)
845
864
  return [from_grpc(runner) for runner in response.runners]
@@ -7,18 +7,19 @@ from typing import TYPE_CHECKING
7
7
  from .image import * # noqa: F403
8
8
 
9
9
  if TYPE_CHECKING:
10
- from PIL.Image import Image as PILImage
10
+ # suffix so we don't clash with PILImage from .image
11
+ from PIL.Image import Image as PILImage2
11
12
 
12
13
 
13
14
  def filter_by(
14
15
  has_nsfw_concepts: list[bool],
15
- images: list[PILImage],
16
- ) -> list[PILImage]:
17
- from PIL import Image as PILImage
16
+ images: list[PILImage2],
17
+ ) -> list[PILImage2]:
18
+ from PIL import Image as PILImageModule
18
19
 
19
20
  return [
20
21
  (
21
- PILImage.new("RGB", (image.width, image.height), (0, 0, 0))
22
+ PILImageModule.new("RGB", (image.width, image.height), (0, 0, 0))
22
23
  if has_nsfw
23
24
  else image
24
25
  )
@@ -619,21 +619,23 @@ def test_worker_env_vars(isolated_client):
619
619
 
620
620
 
621
621
  @pytest.mark.parametrize(
622
- "repo_type, url_prefix",
622
+ "repo_type, url_prefixes",
623
623
  [
624
624
  (
625
625
  "fal",
626
- "https://storage.googleapis.com/isolate-dev-smiling-shark_toolkit_public_bucket/",
626
+ [
627
+ "https://storage.googleapis.com/isolate-dev-smiling-shark_toolkit_public_bucket/"
628
+ ],
627
629
  ),
628
- ("fal_v2", "https://v2.fal.media/files"),
629
- ("fal_v3", "https://v3.fal.media/files"),
630
+ ("fal_v2", ["https://v2.fal.media/files"]),
631
+ ("fal_v3", ["https://v3.fal.media/files", "https://v3b.fal.media/files"]),
630
632
  ],
631
633
  )
632
- def test_fal_storage(isolated_client, repo_type, url_prefix):
634
+ def test_fal_storage(isolated_client, repo_type, url_prefixes):
633
635
  file = File.from_bytes(
634
636
  b"Hello fal storage from local", repository=repo_type, fallback_repository=None
635
637
  )
636
- assert file.url.startswith(url_prefix)
638
+ assert any(file.url.startswith(url_prefix) for url_prefix in url_prefixes)
637
639
  assert file.as_bytes().decode().endswith("local")
638
640
 
639
641
  @isolated_client(
@@ -656,7 +658,7 @@ def test_fal_storage(isolated_client, repo_type, url_prefix):
656
658
 
657
659
  local_fn = hello_file.on(serve=False)
658
660
  file = local_fn()
659
- assert file.url.startswith(url_prefix)
661
+ assert any(file.url.startswith(url_prefix) for url_prefix in url_prefixes)
660
662
  assert file.as_bytes().decode().endswith("isolated")
661
663
 
662
664
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes