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.
- {fal-1.59.0/fal.egg-info → fal-1.61.0}/PKG-INFO +3 -3
- {fal-1.59.0 → fal-1.61.0/fal.egg-info}/PKG-INFO +3 -3
- {fal-1.59.0 → fal-1.61.0}/fal.egg-info/SOURCES.txt +1 -0
- {fal-1.59.0 → fal-1.61.0}/fal.egg-info/requires.txt +2 -2
- {fal-1.59.0 → fal-1.61.0}/pyproject.toml +2 -2
- {fal-1.59.0 → fal-1.61.0}/src/fal/_fal_version.py +2 -2
- fal-1.61.0/src/fal/api/__init__.py +16 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/api/api.py +70 -56
- fal-1.61.0/src/fal/api/client.py +405 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/api/deploy.py +4 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/app.py +93 -4
- {fal-1.59.0 → fal-1.61.0}/src/fal/apps.py +1 -1
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/apps.py +3 -2
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/deploy.py +19 -9
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/queue.py +14 -2
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/run.py +9 -2
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/runners.py +12 -0
- fal-1.61.0/src/fal/exceptions/__init__.py +23 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/exceptions/_base.py +1 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/file_sync.py +23 -30
- {fal-1.59.0 → fal-1.61.0}/src/fal/files.py +21 -58
- {fal-1.59.0 → fal-1.61.0}/src/fal/flags.py +4 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/sdk.py +22 -11
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/__init__.py +21 -5
- fal-1.61.0/src/fal/upload.py +313 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/utils.py +3 -1
- {fal-1.59.0 → fal-1.61.0}/tests/e2e/test_apps.py +292 -19
- {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_deploy.py +35 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_run.py +19 -4
- {fal-1.59.0 → fal-1.61.0}/tests/unit/test_app.py +3 -1
- {fal-1.59.0 → fal-1.61.0}/tests/unit/test_file_sync.py +122 -14
- fal-1.59.0/src/fal/api/__init__.py +0 -2
- fal-1.59.0/src/fal/api/client.py +0 -194
- fal-1.59.0/src/fal/exceptions/__init__.py +0 -11
- {fal-1.59.0 → fal-1.61.0}/.gitignore +0 -0
- {fal-1.59.0 → fal-1.61.0}/Makefile +0 -0
- {fal-1.59.0 → fal-1.61.0}/README.md +0 -0
- {fal-1.59.0 → fal-1.61.0}/docs/conf.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/docs/index.rst +0 -0
- {fal-1.59.0 → fal-1.61.0}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.59.0 → fal-1.61.0}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.59.0 → fal-1.61.0}/fal.egg-info/top_level.txt +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/README.md +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {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
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.59.0 → fal-1.61.0}/openapi_rest.config.yaml +0 -0
- {fal-1.59.0 → fal-1.61.0}/setup.cfg +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/__main__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/_serialization.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/_version.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/api/apps.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/api/keys.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/api/runners.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/api/secrets.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/auth/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/auth/auth0.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/auth/local.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/_utils.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/api.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/auth.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/cli_nested_json.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/create.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/debug.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/doctor.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/files.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/keys.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/main.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/parser.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/profile.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/secrets.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/cli/teams.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/config.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/console/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/console/icons.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/console/ux.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/container.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/distributed/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/distributed/utils.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/distributed/worker.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/exceptions/_cuda.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/exceptions/auth.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/logging/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/logging/isolate.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/logging/style.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/logging/trace.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/project.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/py.typed +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/sync.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/audio/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/audio/audio.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/compilation.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/file.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/providers/fal.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/providers/s3.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/file/types.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/image.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/image/safety_checker.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/kv.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/types.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/download_utils.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/endpoint.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/retry.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/utils/setup_utils.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/video/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/toolkit/video/video.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/src/fal/workflows.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/assets/cat.png +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/conftest.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/integration/test.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/integration/test_files.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/integration/test_stability.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/integration/toolkit/test_image.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/integration/toolkit/test_kv.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_apps.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_auth.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_keys.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/cli/test_secrets.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/distributed/__init__.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/distributed/test_integration.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/distributed/test_utils.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/distributed/test_worker.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/test_utils.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/toolkit/file/providers/test_fal_retry.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/toolkit/test_file.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/toolkit/test_types.py +0 -0
- {fal-1.59.0 → fal-1.61.0}/tests/unit/toolkit/utils/test_retry.py +0 -0
- {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.
|
|
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.
|
|
9
|
-
Requires-Dist: isolate-proto>=0.29.
|
|
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.
|
|
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.
|
|
9
|
-
Requires-Dist: isolate-proto>=0.29.
|
|
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
|
|
@@ -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.
|
|
26
|
-
"isolate-proto>=0.29.
|
|
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.
|
|
32
|
-
__version_tuple__ = version_tuple = (1,
|
|
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
|
-
|
|
705
|
+
from fal.flags import URL_OUTPUT
|
|
706
|
+
|
|
703
707
|
endpoints = getattr(func, "_routes", ["/"]) # type: ignore[attr-defined]
|
|
704
|
-
|
|
705
|
-
console.print(
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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"},
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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())
|
|
1359
|
-
asyncio.create_task(metrics_server.serve())
|
|
1396
|
+
asyncio.create_task(server.serve()),
|
|
1397
|
+
asyncio.create_task(metrics_server.serve()),
|
|
1360
1398
|
}
|
|
1361
1399
|
|
|
1362
|
-
|
|
1363
|
-
tasks
|
|
1364
|
-
return_when=asyncio.
|
|
1400
|
+
await asyncio.wait(
|
|
1401
|
+
tasks,
|
|
1402
|
+
return_when=asyncio.ALL_COMPLETED,
|
|
1365
1403
|
)
|
|
1366
|
-
|
|
1367
|
-
|
|
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
|
-
|
|
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
|
|
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
|