fal 1.26.2__tar.gz → 1.26.4__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 (185) hide show
  1. {fal-1.26.2/fal.egg-info → fal-1.26.4}/PKG-INFO +1 -1
  2. {fal-1.26.2 → fal-1.26.4/fal.egg-info}/PKG-INFO +1 -1
  3. {fal-1.26.2 → fal-1.26.4}/src/fal/_fal_version.py +2 -2
  4. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/profile.py +65 -8
  5. {fal-1.26.2 → fal-1.26.4}/src/fal/config.py +21 -17
  6. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/file/providers/fal.py +24 -32
  7. {fal-1.26.2 → fal-1.26.4}/tests/test_apps.py +5 -5
  8. {fal-1.26.2 → fal-1.26.4}/.gitignore +0 -0
  9. {fal-1.26.2 → fal-1.26.4}/Makefile +0 -0
  10. {fal-1.26.2 → fal-1.26.4}/README.md +0 -0
  11. {fal-1.26.2 → fal-1.26.4}/docs/conf.py +0 -0
  12. {fal-1.26.2 → fal-1.26.4}/docs/index.rst +0 -0
  13. {fal-1.26.2 → fal-1.26.4}/fal.egg-info/SOURCES.txt +0 -0
  14. {fal-1.26.2 → fal-1.26.4}/fal.egg-info/dependency_links.txt +0 -0
  15. {fal-1.26.2 → fal-1.26.4}/fal.egg-info/entry_points.txt +0 -0
  16. {fal-1.26.2 → fal-1.26.4}/fal.egg-info/requires.txt +0 -0
  17. {fal-1.26.2 → fal-1.26.4}/fal.egg-info/top_level.txt +0 -0
  18. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/README.md +0 -0
  19. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  20. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  21. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  22. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  23. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  24. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  25. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  26. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  27. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  28. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  29. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  30. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  31. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  32. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  33. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  34. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  35. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  36. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  37. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  38. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  39. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  40. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  41. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  42. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  43. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  44. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  45. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  46. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  47. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  48. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  49. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  50. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  51. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  52. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  53. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  54. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  55. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  56. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  57. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  58. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  59. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  60. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  61. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  62. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  63. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  64. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  65. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  66. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  67. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  68. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  69. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  70. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  71. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  72. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  73. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  74. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  75. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  76. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  77. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  78. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  79. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  80. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  81. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  82. {fal-1.26.2 → fal-1.26.4}/openapi-fal-rest/pyproject.toml +0 -0
  83. {fal-1.26.2 → fal-1.26.4}/openapi_rest.config.yaml +0 -0
  84. {fal-1.26.2 → fal-1.26.4}/pyproject.toml +0 -0
  85. {fal-1.26.2 → fal-1.26.4}/setup.cfg +0 -0
  86. {fal-1.26.2 → fal-1.26.4}/src/fal/__init__.py +0 -0
  87. {fal-1.26.2 → fal-1.26.4}/src/fal/__main__.py +0 -0
  88. {fal-1.26.2 → fal-1.26.4}/src/fal/_serialization.py +0 -0
  89. {fal-1.26.2 → fal-1.26.4}/src/fal/_version.py +0 -0
  90. {fal-1.26.2 → fal-1.26.4}/src/fal/api.py +0 -0
  91. {fal-1.26.2 → fal-1.26.4}/src/fal/app.py +0 -0
  92. {fal-1.26.2 → fal-1.26.4}/src/fal/apps.py +0 -0
  93. {fal-1.26.2 → fal-1.26.4}/src/fal/auth/__init__.py +0 -0
  94. {fal-1.26.2 → fal-1.26.4}/src/fal/auth/auth0.py +0 -0
  95. {fal-1.26.2 → fal-1.26.4}/src/fal/auth/local.py +0 -0
  96. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/__init__.py +0 -0
  97. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/_utils.py +0 -0
  98. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/api.py +0 -0
  99. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/apps.py +0 -0
  100. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/auth.py +0 -0
  101. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/cli_nested_json.py +0 -0
  102. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/create.py +0 -0
  103. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/debug.py +0 -0
  104. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/deploy.py +0 -0
  105. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/doctor.py +0 -0
  106. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/files.py +0 -0
  107. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/keys.py +0 -0
  108. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/main.py +0 -0
  109. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/parser.py +0 -0
  110. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/run.py +0 -0
  111. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/runners.py +0 -0
  112. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/secrets.py +0 -0
  113. {fal-1.26.2 → fal-1.26.4}/src/fal/cli/teams.py +0 -0
  114. {fal-1.26.2 → fal-1.26.4}/src/fal/console/__init__.py +0 -0
  115. {fal-1.26.2 → fal-1.26.4}/src/fal/console/icons.py +0 -0
  116. {fal-1.26.2 → fal-1.26.4}/src/fal/console/ux.py +0 -0
  117. {fal-1.26.2 → fal-1.26.4}/src/fal/container.py +0 -0
  118. {fal-1.26.2 → fal-1.26.4}/src/fal/exceptions/__init__.py +0 -0
  119. {fal-1.26.2 → fal-1.26.4}/src/fal/exceptions/_base.py +0 -0
  120. {fal-1.26.2 → fal-1.26.4}/src/fal/exceptions/_cuda.py +0 -0
  121. {fal-1.26.2 → fal-1.26.4}/src/fal/exceptions/auth.py +0 -0
  122. {fal-1.26.2 → fal-1.26.4}/src/fal/files.py +0 -0
  123. {fal-1.26.2 → fal-1.26.4}/src/fal/flags.py +0 -0
  124. {fal-1.26.2 → fal-1.26.4}/src/fal/logging/__init__.py +0 -0
  125. {fal-1.26.2 → fal-1.26.4}/src/fal/logging/isolate.py +0 -0
  126. {fal-1.26.2 → fal-1.26.4}/src/fal/logging/style.py +0 -0
  127. {fal-1.26.2 → fal-1.26.4}/src/fal/logging/trace.py +0 -0
  128. {fal-1.26.2 → fal-1.26.4}/src/fal/logging/user.py +0 -0
  129. {fal-1.26.2 → fal-1.26.4}/src/fal/project.py +0 -0
  130. {fal-1.26.2 → fal-1.26.4}/src/fal/py.typed +0 -0
  131. {fal-1.26.2 → fal-1.26.4}/src/fal/rest_client.py +0 -0
  132. {fal-1.26.2 → fal-1.26.4}/src/fal/sdk.py +0 -0
  133. {fal-1.26.2 → fal-1.26.4}/src/fal/sync.py +0 -0
  134. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/__init__.py +0 -0
  135. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/audio/__init__.py +0 -0
  136. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/audio/audio.py +0 -0
  137. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/exceptions.py +0 -0
  138. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/file/__init__.py +0 -0
  139. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/file/file.py +0 -0
  140. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/file/providers/gcp.py +0 -0
  141. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/file/providers/r2.py +0 -0
  142. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/file/providers/s3.py +0 -0
  143. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/file/types.py +0 -0
  144. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/image/__init__.py +0 -0
  145. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/image/image.py +0 -0
  146. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  147. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  148. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  149. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  150. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  151. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/image/safety_checker.py +0 -0
  152. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/kv.py +0 -0
  153. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/optimize.py +0 -0
  154. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/types.py +0 -0
  155. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/utils/__init__.py +0 -0
  156. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/utils/download_utils.py +0 -0
  157. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/utils/endpoint.py +0 -0
  158. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/utils/retry.py +0 -0
  159. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/video/__init__.py +0 -0
  160. {fal-1.26.2 → fal-1.26.4}/src/fal/toolkit/video/video.py +0 -0
  161. {fal-1.26.2 → fal-1.26.4}/src/fal/utils.py +0 -0
  162. {fal-1.26.2 → fal-1.26.4}/src/fal/workflows.py +0 -0
  163. {fal-1.26.2 → fal-1.26.4}/tests/__init__.py +0 -0
  164. {fal-1.26.2 → fal-1.26.4}/tests/assets/cat.png +0 -0
  165. {fal-1.26.2 → fal-1.26.4}/tests/cli/__init__.py +0 -0
  166. {fal-1.26.2 → fal-1.26.4}/tests/cli/test_apps.py +0 -0
  167. {fal-1.26.2 → fal-1.26.4}/tests/cli/test_auth.py +0 -0
  168. {fal-1.26.2 → fal-1.26.4}/tests/cli/test_deploy.py +0 -0
  169. {fal-1.26.2 → fal-1.26.4}/tests/cli/test_keys.py +0 -0
  170. {fal-1.26.2 → fal-1.26.4}/tests/cli/test_run.py +0 -0
  171. {fal-1.26.2 → fal-1.26.4}/tests/cli/test_secrets.py +0 -0
  172. {fal-1.26.2 → fal-1.26.4}/tests/conftest.py +0 -0
  173. {fal-1.26.2 → fal-1.26.4}/tests/integration_test.py +0 -0
  174. {fal-1.26.2 → fal-1.26.4}/tests/mainify_package/__init__.py +0 -0
  175. {fal-1.26.2 → fal-1.26.4}/tests/mainify_package/impl.py +0 -0
  176. {fal-1.26.2 → fal-1.26.4}/tests/mainify_package/utils.py +0 -0
  177. {fal-1.26.2 → fal-1.26.4}/tests/mainify_target.py +0 -0
  178. {fal-1.26.2 → fal-1.26.4}/tests/test_kv.py +0 -0
  179. {fal-1.26.2 → fal-1.26.4}/tests/test_stability.py +0 -0
  180. {fal-1.26.2 → fal-1.26.4}/tests/toolkit/file/providers/test_fal_retry.py +0 -0
  181. {fal-1.26.2 → fal-1.26.4}/tests/toolkit/file_test.py +0 -0
  182. {fal-1.26.2 → fal-1.26.4}/tests/toolkit/image_test.py +0 -0
  183. {fal-1.26.2 → fal-1.26.4}/tests/toolkit/test_types.py +0 -0
  184. {fal-1.26.2 → fal-1.26.4}/tests/toolkit/utils/retry.py +0 -0
  185. {fal-1.26.2 → fal-1.26.4}/tools/demo_script.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.26.2
3
+ Version: 1.26.4
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.26.2
3
+ Version: 1.26.4
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
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.26.2'
21
- __version_tuple__ = version_tuple = (1, 26, 2)
20
+ __version__ = version = '1.26.4'
21
+ __version_tuple__ = version_tuple = (1, 26, 4)
@@ -23,14 +23,38 @@ def _list(args):
23
23
 
24
24
 
25
25
  def _set(args):
26
- with Config().edit() as config:
27
- config.set_internal("profile", args.PROFILE)
28
- args.console.print(f"Default profile set to [cyan]{args.PROFILE}[/].")
29
- config.profile = args.PROFILE
30
- if not config.get("key"):
26
+ config = Config()
27
+
28
+ # Check if the profile exists
29
+ if args.PROFILE not in config._config:
30
+ # Profile doesn't exist, offer to create it
31
+ args.console.print(f"Profile [cyan]{args.PROFILE}[/] does not exist.")
32
+ create_profile = input("Would you like to create it? (y/N): ").strip().lower()
33
+
34
+ if create_profile in ["y", "yes"]:
35
+ # Create the profile by setting it
36
+ with config.edit() as config:
37
+ config.set_internal("profile", args.PROFILE)
38
+ config._config[args.PROFILE] = {}
39
+ config._profile = args.PROFILE
31
40
  args.console.print(
32
- "No key set for profile. Use [bold]fal profile key[/] to set a key."
41
+ f"Profile [cyan]{args.PROFILE}[/] created and set as default."
33
42
  )
43
+ else:
44
+ args.console.print("Profile creation cancelled.")
45
+ return
46
+ else:
47
+ # Profile exists, just set it as default
48
+ with config.edit() as config:
49
+ config.set_internal("profile", args.PROFILE)
50
+ config._profile = args.PROFILE
51
+ args.console.print(f"Default profile set to [cyan]{args.PROFILE}[/].")
52
+
53
+ # Check if key is set for the profile
54
+ if not config.get("key"):
55
+ args.console.print(
56
+ "No key set for profile. Use [bold]fal profile key[/] to set a key."
57
+ )
34
58
 
35
59
 
36
60
  def _unset(args, config: Config | None = None):
@@ -42,6 +66,8 @@ def _unset(args, config: Config | None = None):
42
66
 
43
67
 
44
68
  def _key_set(args):
69
+ config = Config(validate_profile=True)
70
+
45
71
  while True:
46
72
  key = input("Enter the key: ")
47
73
  if ":" in key:
@@ -50,7 +76,7 @@ def _key_set(args):
50
76
  "[red]Invalid key. The key must be in the format [bold]key:value[/].[/]"
51
77
  )
52
78
 
53
- with Config().edit() as config:
79
+ with config.edit():
54
80
  config.set("key", key)
55
81
  args.console.print(f"Key set for profile [cyan]{config.profile}[/].")
56
82
 
@@ -61,6 +87,24 @@ def _host_set(args):
61
87
  args.console.print(f"Fal host set to [cyan]{args.HOST}[/].")
62
88
 
63
89
 
90
+ def _create(args):
91
+ config = Config()
92
+
93
+ # Check if the profile already exists
94
+ if args.PROFILE in config._config:
95
+ args.console.print(f"Profile [cyan]{args.PROFILE}[/] already exists.")
96
+ return
97
+
98
+ # Create the profile
99
+ with config.edit() as config:
100
+ config._config[args.PROFILE] = {}
101
+
102
+ args.console.print(f"Profile [cyan]{args.PROFILE}[/] created.")
103
+ args.console.print(
104
+ f"Use [bold]fal profile set {args.PROFILE}[/] to set it as default."
105
+ )
106
+
107
+
64
108
  def _delete(args):
65
109
  with Config().edit() as config:
66
110
  if config.profile == args.PROFILE:
@@ -96,7 +140,7 @@ def add_parser(main_subparsers, parents):
96
140
  )
97
141
  list_parser.set_defaults(func=_list)
98
142
 
99
- set_help = "Set default profile."
143
+ set_help = "Set default profile. If the profile doesn't exist, you'll be prompted to create it." # noqa: E501
100
144
  set_parser = subparsers.add_parser(
101
145
  "set",
102
146
  description=set_help,
@@ -140,6 +184,19 @@ def add_parser(main_subparsers, parents):
140
184
  )
141
185
  host_set_parser.set_defaults(func=_host_set)
142
186
 
187
+ create_help = "Create a new profile. Use 'fal profile set <name>' to set it as default after creation." # noqa: E501
188
+ create_parser = subparsers.add_parser(
189
+ "create",
190
+ description=create_help,
191
+ help=create_help,
192
+ parents=parents,
193
+ )
194
+ create_parser.add_argument(
195
+ "PROFILE",
196
+ help="Profile name.",
197
+ )
198
+ create_parser.set_defaults(func=_create)
199
+
143
200
  delete_help = "Delete profile."
144
201
  delete_parser = subparsers.add_parser(
145
202
  "delete",
@@ -4,8 +4,9 @@ import os
4
4
  from contextlib import contextmanager
5
5
  from typing import Dict, Iterator, List, Optional
6
6
 
7
- SETTINGS_SECTION = "__internal__" # legacy
8
- DEFAULT_PROFILE = "default"
7
+ SETTINGS_SECTION = "__internal__"
8
+
9
+ NO_PROFILE_ERROR = ValueError("No profile set.")
9
10
 
10
11
 
11
12
  class Config:
@@ -15,7 +16,7 @@ class Config:
15
16
 
16
17
  DEFAULT_CONFIG_PATH = "~/.fal/config.toml"
17
18
 
18
- def __init__(self):
19
+ def __init__(self, *, validate_profile: bool = False):
19
20
  import tomli
20
21
 
21
22
  self.config_path = os.path.expanduser(
@@ -28,11 +29,17 @@ class Config:
28
29
  except FileNotFoundError:
29
30
  self._config = {}
30
31
 
31
- profile = (
32
- os.getenv("FAL_PROFILE") or self.get_internal("profile") or DEFAULT_PROFILE
33
- )
32
+ profile = os.getenv("FAL_PROFILE") or self.get_internal("profile")
33
+
34
+ # Try to set the profile, but don't fail if it doesn't exist
35
+ try:
36
+ self.profile = profile
37
+ except ValueError:
38
+ # Profile doesn't exist, set to None
39
+ self._profile = None
34
40
 
35
- self.profile = profile
41
+ if validate_profile and not self.profile:
42
+ raise NO_PROFILE_ERROR
36
43
 
37
44
  @property
38
45
  def profile(self) -> Optional[str]:
@@ -41,9 +48,10 @@ class Config:
41
48
  @profile.setter
42
49
  def profile(self, value: Optional[str]) -> None:
43
50
  if value and value not in self._config:
44
- # Make sure the section exists
45
- self._config[value] = {}
46
- self.set_internal("profile", value)
51
+ # Don't automatically create profiles - they should be created explicitly
52
+ raise ValueError(
53
+ f"Profile '{value}' does not exist. Create it first or use the profile set command." # noqa: E501
54
+ )
47
55
  elif not value:
48
56
  self.unset_internal("profile")
49
57
 
@@ -53,11 +61,7 @@ class Config:
53
61
  keys: List[str] = []
54
62
  for key in self._config:
55
63
  if key != SETTINGS_SECTION:
56
- if key == DEFAULT_PROFILE:
57
- # Add it at the beginning
58
- keys.insert(0, key)
59
- else:
60
- keys.append(key)
64
+ keys.append(key)
61
65
 
62
66
  return keys
63
67
 
@@ -75,13 +79,13 @@ class Config:
75
79
 
76
80
  def set(self, key: str, value: str) -> None:
77
81
  if not self.profile:
78
- raise ValueError("No profile set.")
82
+ raise NO_PROFILE_ERROR
79
83
 
80
84
  self._config[self.profile][key] = value
81
85
 
82
86
  def unset(self, key: str) -> None:
83
87
  if not self.profile:
84
- raise ValueError("No profile set.")
88
+ raise NO_PROFILE_ERROR
85
89
 
86
90
  self._config.get(self.profile, {}).pop(key, None)
87
91
 
@@ -9,7 +9,7 @@ from contextlib import contextmanager
9
9
  from dataclasses import dataclass
10
10
  from datetime import datetime, timezone
11
11
  from pathlib import Path
12
- from typing import Any, Generator, Generic, TypeVar
12
+ from typing import Any, Dict, Generator, Generic, TypeVar
13
13
  from urllib.error import HTTPError
14
14
  from urllib.parse import urlparse, urlunparse
15
15
  from urllib.request import Request, urlopen
@@ -45,6 +45,9 @@ def _should_retry(exc: Exception) -> bool:
45
45
  if isinstance(exc, HTTPError) and exc.code in RETRY_CODES:
46
46
  return True
47
47
 
48
+ if isinstance(exc, TimeoutError):
49
+ return True
50
+
48
51
  return False
49
52
 
50
53
 
@@ -66,6 +69,19 @@ def _maybe_retry_request(
66
69
  yield response
67
70
 
68
71
 
72
+ def _object_lifecycle_headers(
73
+ headers: dict[str, str],
74
+ object_lifecycle_preference: dict[str, str] | None,
75
+ ):
76
+ if object_lifecycle_preference:
77
+ # Used by V3 CDN
78
+ headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
79
+ # Used by V1 CDN
80
+ headers["X-Fal-Object-Lifecycle-Preference"] = json.dumps(
81
+ object_lifecycle_preference
82
+ )
83
+
84
+
69
85
  @dataclass
70
86
  class FalV2Token:
71
87
  token: str
@@ -448,14 +464,6 @@ class MultipartUploadGCS:
448
464
 
449
465
  @dataclass
450
466
  class FalFileRepository(FalFileRepositoryBase):
451
- def _object_lifecycle_headers(
452
- self,
453
- headers: dict[str, str],
454
- object_lifecycle_preference: dict[str, str] | None,
455
- ):
456
- if object_lifecycle_preference:
457
- headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
458
-
459
467
  def save(
460
468
  self,
461
469
  file: FileData,
@@ -476,9 +484,8 @@ class FalFileRepository(FalFileRepositoryBase):
476
484
  max_concurrency=multipart_max_concurrency,
477
485
  )
478
486
 
479
- headers = {}
480
- if object_lifecycle_preference:
481
- headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
487
+ headers: Dict[str, str] = {}
488
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
482
489
 
483
490
  return self._save(file, "gcs", headers=headers)
484
491
 
@@ -1121,6 +1128,8 @@ class FalFileRepositoryV2(FalFileRepositoryBase):
1121
1128
  "Content-Type": file.content_type,
1122
1129
  }
1123
1130
 
1131
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
1132
+
1124
1133
  storage_url = f"{token.base_upload_url}/upload"
1125
1134
 
1126
1135
  try:
@@ -1192,16 +1201,6 @@ class InMemoryRepository(FileRepository):
1192
1201
 
1193
1202
  @dataclass
1194
1203
  class FalCDNFileRepository(FileRepository):
1195
- def _object_lifecycle_headers(
1196
- self,
1197
- headers: dict[str, str],
1198
- object_lifecycle_preference: dict[str, str] | None,
1199
- ):
1200
- if object_lifecycle_preference:
1201
- headers["X-Fal-Object-Lifecycle-Preference"] = json.dumps(
1202
- object_lifecycle_preference
1203
- )
1204
-
1205
1204
  def save(
1206
1205
  self,
1207
1206
  file: FileData,
@@ -1218,7 +1217,7 @@ class FalCDNFileRepository(FileRepository):
1218
1217
  "X-Fal-File-Name": file.file_name,
1219
1218
  }
1220
1219
 
1221
- self._object_lifecycle_headers(headers, object_lifecycle_preference)
1220
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
1222
1221
 
1223
1222
  url = os.getenv("FAL_CDN_HOST", _FAL_CDN) + "/files/upload"
1224
1223
  request = Request(url, headers=headers, method="POST", data=file.data)
@@ -1285,6 +1284,7 @@ class FalFileRepositoryV3(FileRepository):
1285
1284
  "Accept": "application/json",
1286
1285
  "Content-Type": "application/json",
1287
1286
  }
1287
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
1288
1288
 
1289
1289
  grpc_host = os.environ.get("FAL_HOST", "api.alpha.fal.ai")
1290
1290
  rest_host = grpc_host.replace("api", "rest", 1)
@@ -1373,14 +1373,6 @@ class InternalFalFileRepositoryV3(FileRepository):
1373
1373
  That way it can avoid the need to refresh the token for every upload.
1374
1374
  """
1375
1375
 
1376
- def _object_lifecycle_headers(
1377
- self,
1378
- headers: dict[str, str],
1379
- object_lifecycle_preference: dict[str, str] | None,
1380
- ):
1381
- if object_lifecycle_preference:
1382
- headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
1383
-
1384
1376
  def save(
1385
1377
  self,
1386
1378
  file: FileData,
@@ -1410,7 +1402,7 @@ class InternalFalFileRepositoryV3(FileRepository):
1410
1402
  "X-Fal-File-Name": file.file_name,
1411
1403
  }
1412
1404
 
1413
- self._object_lifecycle_headers(headers, object_lifecycle_preference)
1405
+ _object_lifecycle_headers(headers, object_lifecycle_preference)
1414
1406
 
1415
1407
  url = os.getenv("FAL_CDN_V3_HOST", _FAL_CDN_V3) + "/files/upload"
1416
1408
  request = Request(url, headers=headers, method="POST", data=file.data)
@@ -235,7 +235,7 @@ class ExceptionApp(fal.App, keep_alive=300, max_concurrency=1):
235
235
  raise RuntimeError("cuDNN error: CUDNN_STATUS_INTERNAL_ERROR")
236
236
 
237
237
 
238
- class CancellableApp(fal.App, keep_alive=300, max_concurrency=1, request_timeout=2):
238
+ class CancellableApp(fal.App, keep_alive=300, max_concurrency=1, request_timeout=4):
239
239
  task = None
240
240
  running = 0
241
241
 
@@ -514,12 +514,12 @@ def test_stateful_app_client(test_stateful_app: str):
514
514
 
515
515
  def test_app_cancellation(test_app: str, test_cancellable_app: str):
516
516
  request_handle = apps.submit(
517
- test_cancellable_app, arguments={"lhs": 1, "rhs": 2, "wait_time": 5}
517
+ test_cancellable_app, arguments={"lhs": 1, "rhs": 2, "wait_time": 6}
518
518
  )
519
519
 
520
520
  while True:
521
521
  status = request_handle.status()
522
- time.sleep(0.5)
522
+ time.sleep(0.05)
523
523
  if isinstance(status, apps.InProgress):
524
524
  # The app is running
525
525
  break
@@ -534,12 +534,12 @@ def test_app_cancellation(test_app: str, test_cancellable_app: str):
534
534
 
535
535
  # normal app should just ignore the cancellation
536
536
  request_handle = apps.submit(
537
- test_app, arguments={"lhs": 1, "rhs": 2, "wait_time": 5}
537
+ test_app, arguments={"lhs": 1, "rhs": 2, "wait_time": 6}
538
538
  )
539
539
 
540
540
  while True:
541
541
  status = request_handle.status()
542
- time.sleep(0.5)
542
+ time.sleep(0.05)
543
543
  if isinstance(status, apps.InProgress):
544
544
  # The app is running
545
545
  break
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
File without changes
File without changes
File without changes