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