fal 1.5.2__tar.gz → 1.5.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.
- {fal-1.5.2 → fal-1.5.4}/.gitignore +1 -0
- fal-1.5.4/Makefile +24 -0
- {fal-1.5.2 → fal-1.5.4}/PKG-INFO +7 -2
- fal-1.5.4/docs/conf.py +31 -0
- fal-1.5.4/docs/index.rst +18 -0
- {fal-1.5.2 → fal-1.5.4}/fal.egg-info/PKG-INFO +7 -2
- {fal-1.5.2 → fal-1.5.4}/fal.egg-info/SOURCES.txt +3 -0
- {fal-1.5.2 → fal-1.5.4}/fal.egg-info/requires.txt +7 -1
- {fal-1.5.2 → fal-1.5.4}/pyproject.toml +7 -1
- {fal-1.5.2 → fal-1.5.4}/src/fal/_fal_version.py +2 -2
- {fal-1.5.2 → fal-1.5.4}/src/fal/api.py +1 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/app.py +100 -11
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/file/file.py +3 -3
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/file/providers/fal.py +200 -16
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/file/providers/gcp.py +6 -1
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/file/providers/r2.py +6 -1
- {fal-1.5.2 → fal-1.5.4}/tests/test_apps.py +9 -9
- {fal-1.5.2 → fal-1.5.4}/README.md +0 -0
- {fal-1.5.2 → fal-1.5.4}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.5.2 → fal-1.5.4}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.5.2 → fal-1.5.4}/fal.egg-info/top_level.txt +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/README.md +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.5.2 → fal-1.5.4}/openapi_rest.config.yaml +0 -0
- {fal-1.5.2 → fal-1.5.4}/setup.cfg +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/__main__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/_serialization.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/_version.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/apps.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/auth/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/auth/auth0.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/auth/local.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/_utils.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/apps.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/auth.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/create.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/debug.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/deploy.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/doctor.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/keys.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/main.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/parser.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/run.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/cli/secrets.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/console/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/console/icons.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/console/ux.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/container.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/exceptions/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/exceptions/_base.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/exceptions/_cuda.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/exceptions/auth.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/files.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/flags.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/logging/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/logging/isolate.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/logging/style.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/logging/trace.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/logging/user.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/py.typed +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/rest_client.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/sdk.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/sync.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/file/types.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/image/image.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/image/safety_checker.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/utils/download_utils.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/toolkit/utils/retry.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/utils.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/src/fal/workflows.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/assets/cat.png +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/cli/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/cli/test_apps.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/cli/test_auth.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/cli/test_deploy.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/cli/test_keys.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/cli/test_run.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/cli/test_secrets.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/conftest.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/integration_test.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/mainify_package/__init__.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/mainify_package/impl.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/mainify_package/utils.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/mainify_target.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/test_stability.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/toolkit/file_test.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/toolkit/image_test.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tests/toolkit/utils/retry.py +0 -0
- {fal-1.5.2 → fal-1.5.4}/tools/demo_script.py +0 -0
fal-1.5.4/Makefile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MAKEFILE_DIR := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
|
2
|
+
|
|
3
|
+
SPHINXOPTS = --fresh-env
|
|
4
|
+
SPHINXBUILD = sphinx-build
|
|
5
|
+
SPHINXAPIDOC = sphinx-apidoc
|
|
6
|
+
SOURCEDIR = $(MAKEFILE_DIR)/docs
|
|
7
|
+
BUILDDIR = $(MAKEFILE_DIR)/docs/_build
|
|
8
|
+
PROJECTDIR = $(MAKEFILE_DIR)/src/fal
|
|
9
|
+
|
|
10
|
+
help:
|
|
11
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
12
|
+
|
|
13
|
+
.PHONY: help Makefile clean apidoc html docs
|
|
14
|
+
|
|
15
|
+
clean:
|
|
16
|
+
rm -rf $(BUILDDIR)/*
|
|
17
|
+
|
|
18
|
+
apidoc:
|
|
19
|
+
$(SPHINXAPIDOC) -f -o "$(SOURCEDIR)" "$(PROJECTDIR)"
|
|
20
|
+
|
|
21
|
+
html:
|
|
22
|
+
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
23
|
+
|
|
24
|
+
docs: apidoc html
|
{fal-1.5.2 → fal-1.5.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.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
|
|
@@ -23,6 +23,7 @@ Requires-Dist: rich_argparse
|
|
|
23
23
|
Requires-Dist: packaging>=21.3
|
|
24
24
|
Requires-Dist: pathspec<1,>=0.11.1
|
|
25
25
|
Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3
|
|
26
|
+
Requires-Dist: structlog>=22.0
|
|
26
27
|
Requires-Dist: fastapi<1,>=0.99.1
|
|
27
28
|
Requires-Dist: starlette-exporter>=0.21.0
|
|
28
29
|
Requires-Dist: httpx>=0.15.4
|
|
@@ -37,6 +38,10 @@ Requires-Dist: pyjwt[crypto]<3,>=2.8.0
|
|
|
37
38
|
Requires-Dist: uvicorn<1,>=0.29.0
|
|
38
39
|
Requires-Dist: cookiecutter
|
|
39
40
|
Requires-Dist: tomli
|
|
41
|
+
Provides-Extra: docs
|
|
42
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
43
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
44
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
40
45
|
Provides-Extra: test
|
|
41
46
|
Requires-Dist: pytest<8; extra == "test"
|
|
42
47
|
Requires-Dist: pytest-asyncio; extra == "test"
|
|
@@ -44,7 +49,7 @@ Requires-Dist: pytest-xdist; extra == "test"
|
|
|
44
49
|
Requires-Dist: flaky; extra == "test"
|
|
45
50
|
Requires-Dist: boto3; extra == "test"
|
|
46
51
|
Provides-Extra: dev
|
|
47
|
-
Requires-Dist: fal[test]; extra == "dev"
|
|
52
|
+
Requires-Dist: fal[docs,test]; extra == "dev"
|
|
48
53
|
Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
|
|
49
54
|
|
|
50
55
|
[](https://pypi.org/project/fal)
|
fal-1.5.4/docs/conf.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# For the full list of built-in configuration values, see the documentation:
|
|
4
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
5
|
+
|
|
6
|
+
# -- Project information -----------------------------------------------------
|
|
7
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
8
|
+
|
|
9
|
+
project = 'fal_sdk'
|
|
10
|
+
copyright = '2024, FAL Team'
|
|
11
|
+
author = 'FAL Team'
|
|
12
|
+
|
|
13
|
+
# -- General configuration ---------------------------------------------------
|
|
14
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
15
|
+
|
|
16
|
+
extensions = [
|
|
17
|
+
'sphinx.ext.autodoc',
|
|
18
|
+
'sphinx.ext.napoleon',
|
|
19
|
+
'sphinx_autodoc_typehints'
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
templates_path = ['_templates']
|
|
23
|
+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# -- Options for HTML output -------------------------------------------------
|
|
28
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
29
|
+
|
|
30
|
+
html_theme = 'alabaster'
|
|
31
|
+
html_static_path = ['_static']
|
fal-1.5.4/docs/index.rst
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.. fal_sdk documentation master file, created by
|
|
2
|
+
sphinx-quickstart on Wed Oct 2 05:09:49 2024.
|
|
3
|
+
You can adapt this file completely to your liking, but it should at least
|
|
4
|
+
contain the root `toctree` directive.
|
|
5
|
+
|
|
6
|
+
fal_sdk documentation
|
|
7
|
+
=====================
|
|
8
|
+
|
|
9
|
+
Add your content using ``reStructuredText`` syntax. See the
|
|
10
|
+
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
|
|
11
|
+
documentation for details.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
.. toctree::
|
|
15
|
+
:maxdepth: 2
|
|
16
|
+
:caption: Contents:
|
|
17
|
+
|
|
18
|
+
modules
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.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
|
|
@@ -23,6 +23,7 @@ Requires-Dist: rich_argparse
|
|
|
23
23
|
Requires-Dist: packaging>=21.3
|
|
24
24
|
Requires-Dist: pathspec<1,>=0.11.1
|
|
25
25
|
Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3
|
|
26
|
+
Requires-Dist: structlog>=22.0
|
|
26
27
|
Requires-Dist: fastapi<1,>=0.99.1
|
|
27
28
|
Requires-Dist: starlette-exporter>=0.21.0
|
|
28
29
|
Requires-Dist: httpx>=0.15.4
|
|
@@ -37,6 +38,10 @@ Requires-Dist: pyjwt[crypto]<3,>=2.8.0
|
|
|
37
38
|
Requires-Dist: uvicorn<1,>=0.29.0
|
|
38
39
|
Requires-Dist: cookiecutter
|
|
39
40
|
Requires-Dist: tomli
|
|
41
|
+
Provides-Extra: docs
|
|
42
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
43
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
44
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
40
45
|
Provides-Extra: test
|
|
41
46
|
Requires-Dist: pytest<8; extra == "test"
|
|
42
47
|
Requires-Dist: pytest-asyncio; extra == "test"
|
|
@@ -44,7 +49,7 @@ Requires-Dist: pytest-xdist; extra == "test"
|
|
|
44
49
|
Requires-Dist: flaky; extra == "test"
|
|
45
50
|
Requires-Dist: boto3; extra == "test"
|
|
46
51
|
Provides-Extra: dev
|
|
47
|
-
Requires-Dist: fal[test]; extra == "dev"
|
|
52
|
+
Requires-Dist: fal[docs,test]; extra == "dev"
|
|
48
53
|
Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
|
|
49
54
|
|
|
50
55
|
[](https://pypi.org/project/fal)
|
|
@@ -16,6 +16,7 @@ rich_argparse
|
|
|
16
16
|
packaging>=21.3
|
|
17
17
|
pathspec<1,>=0.11.1
|
|
18
18
|
pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3
|
|
19
|
+
structlog>=22.0
|
|
19
20
|
fastapi<1,>=0.99.1
|
|
20
21
|
starlette-exporter>=0.21.0
|
|
21
22
|
httpx>=0.15.4
|
|
@@ -34,9 +35,14 @@ tomli
|
|
|
34
35
|
importlib-metadata>=4.4
|
|
35
36
|
|
|
36
37
|
[dev]
|
|
37
|
-
fal[test]
|
|
38
|
+
fal[docs,test]
|
|
38
39
|
openapi-python-client<1,>=0.14.1
|
|
39
40
|
|
|
41
|
+
[docs]
|
|
42
|
+
sphinx
|
|
43
|
+
sphinx-rtd-theme
|
|
44
|
+
sphinx-autodoc-typehints
|
|
45
|
+
|
|
40
46
|
[test]
|
|
41
47
|
pytest<8
|
|
42
48
|
pytest-asyncio
|
|
@@ -41,6 +41,7 @@ dependencies = [
|
|
|
41
41
|
"pathspec>=0.11.1,<1",
|
|
42
42
|
"pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3",
|
|
43
43
|
# serve=True dependencies
|
|
44
|
+
"structlog>=22.0",
|
|
44
45
|
"fastapi>=0.99.1,<1",
|
|
45
46
|
"starlette-exporter>=0.21.0",
|
|
46
47
|
# rest-api-client dependencies
|
|
@@ -60,6 +61,11 @@ dependencies = [
|
|
|
60
61
|
]
|
|
61
62
|
|
|
62
63
|
[project.optional-dependencies]
|
|
64
|
+
docs = [
|
|
65
|
+
"sphinx",
|
|
66
|
+
"sphinx-rtd-theme",
|
|
67
|
+
"sphinx-autodoc-typehints",
|
|
68
|
+
]
|
|
63
69
|
test = [
|
|
64
70
|
"pytest<8",
|
|
65
71
|
"pytest-asyncio",
|
|
@@ -68,7 +74,7 @@ test = [
|
|
|
68
74
|
"boto3",
|
|
69
75
|
]
|
|
70
76
|
dev = [
|
|
71
|
-
"fal[test]",
|
|
77
|
+
"fal[docs,test]",
|
|
72
78
|
"openapi-python-client>=0.14.1,<1",
|
|
73
79
|
]
|
|
74
80
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import inspect
|
|
4
5
|
import json
|
|
5
6
|
import os
|
|
@@ -8,20 +9,25 @@ import re
|
|
|
8
9
|
import threading
|
|
9
10
|
import time
|
|
10
11
|
import typing
|
|
11
|
-
from contextlib import asynccontextmanager, contextmanager
|
|
12
|
+
from contextlib import AsyncExitStack, asynccontextmanager, contextmanager
|
|
13
|
+
from dataclasses import dataclass
|
|
12
14
|
from typing import Any, Callable, ClassVar, Literal, TypeVar
|
|
13
15
|
|
|
16
|
+
import grpc.aio as async_grpc
|
|
14
17
|
import httpx
|
|
15
18
|
from fastapi import FastAPI
|
|
19
|
+
from isolate.server import definitions
|
|
16
20
|
|
|
17
21
|
import fal.api
|
|
18
22
|
from fal._serialization import include_modules_from
|
|
19
23
|
from fal.api import RouteSignature
|
|
20
|
-
from fal.exceptions import RequestCancelledException
|
|
24
|
+
from fal.exceptions import FalServerlessException, RequestCancelledException
|
|
21
25
|
from fal.logging import get_logger
|
|
22
|
-
from fal.toolkit.file
|
|
26
|
+
from fal.toolkit.file import get_lifecycle_preference
|
|
27
|
+
from fal.toolkit.file.providers.fal import GLOBAL_LIFECYCLE_PREFERENCE
|
|
23
28
|
|
|
24
29
|
REALTIME_APP_REQUIREMENTS = ["websockets", "msgpack"]
|
|
30
|
+
REQUEST_ID_KEY = "x-fal-request-id"
|
|
25
31
|
|
|
26
32
|
EndpointT = TypeVar("EndpointT", bound=Callable[..., Any])
|
|
27
33
|
logger = get_logger(__name__)
|
|
@@ -34,6 +40,48 @@ async def _call_any_fn(fn, *args, **kwargs):
|
|
|
34
40
|
return fn(*args, **kwargs)
|
|
35
41
|
|
|
36
42
|
|
|
43
|
+
async def open_isolate_channel(address: str) -> async_grpc.Channel:
|
|
44
|
+
_stack = AsyncExitStack()
|
|
45
|
+
channel = await _stack.enter_async_context(
|
|
46
|
+
async_grpc.insecure_channel(
|
|
47
|
+
address,
|
|
48
|
+
options=[
|
|
49
|
+
("grpc.max_send_message_length", -1),
|
|
50
|
+
("grpc.max_receive_message_length", -1),
|
|
51
|
+
("grpc.min_reconnect_backoff_ms", 0),
|
|
52
|
+
("grpc.max_reconnect_backoff_ms", 100),
|
|
53
|
+
("grpc.dns_min_time_between_resolutions_ms", 100),
|
|
54
|
+
],
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
channel_status = channel.channel_ready()
|
|
59
|
+
try:
|
|
60
|
+
await asyncio.wait_for(channel_status, timeout=1)
|
|
61
|
+
except asyncio.TimeoutError:
|
|
62
|
+
await _stack.aclose()
|
|
63
|
+
raise Exception("Timed out trying to connect to local isolate")
|
|
64
|
+
|
|
65
|
+
return channel
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def _set_logger_labels(
|
|
69
|
+
logger_labels: dict[str, str], channel: async_grpc.Channel
|
|
70
|
+
):
|
|
71
|
+
try:
|
|
72
|
+
isolate = definitions.IsolateStub(channel)
|
|
73
|
+
isolate_request = definitions.SetMetadataRequest(
|
|
74
|
+
# TODO: when submit is shipped, get task_id from an env var
|
|
75
|
+
task_id="RUN",
|
|
76
|
+
metadata=definitions.TaskMetadata(logger_labels=logger_labels),
|
|
77
|
+
)
|
|
78
|
+
res = isolate.SetMetadata(isolate_request)
|
|
79
|
+
code = await res.code()
|
|
80
|
+
assert str(code) == "StatusCode.OK"
|
|
81
|
+
except BaseException:
|
|
82
|
+
logger.exception("Failed to set logger labels")
|
|
83
|
+
|
|
84
|
+
|
|
37
85
|
def wrap_app(cls: type[App], **kwargs) -> fal.api.IsolatedFunction:
|
|
38
86
|
include_modules_from(cls)
|
|
39
87
|
|
|
@@ -75,6 +123,12 @@ def wrap_app(cls: type[App], **kwargs) -> fal.api.IsolatedFunction:
|
|
|
75
123
|
return fn
|
|
76
124
|
|
|
77
125
|
|
|
126
|
+
@dataclass
|
|
127
|
+
class AppClientError(FalServerlessException):
|
|
128
|
+
message: str
|
|
129
|
+
status_code: int
|
|
130
|
+
|
|
131
|
+
|
|
78
132
|
class EndpointClient:
|
|
79
133
|
def __init__(self, url, endpoint, signature, timeout: int | None = None):
|
|
80
134
|
self.url = url
|
|
@@ -87,12 +141,19 @@ class EndpointClient:
|
|
|
87
141
|
|
|
88
142
|
def __call__(self, data):
|
|
89
143
|
with httpx.Client() as client:
|
|
144
|
+
url = self.url + self.signature.path
|
|
90
145
|
resp = client.post(
|
|
91
146
|
self.url + self.signature.path,
|
|
92
147
|
json=data.dict() if hasattr(data, "dict") else dict(data),
|
|
93
148
|
timeout=self.timeout,
|
|
94
149
|
)
|
|
95
|
-
resp.
|
|
150
|
+
if not resp.is_success:
|
|
151
|
+
# allow logs to be printed before raising the exception
|
|
152
|
+
time.sleep(1)
|
|
153
|
+
raise AppClientError(
|
|
154
|
+
f"Failed to POST {url}: {resp.status_code} {resp.text}",
|
|
155
|
+
status_code=resp.status_code,
|
|
156
|
+
)
|
|
96
157
|
resp_dict = resp.json()
|
|
97
158
|
|
|
98
159
|
if not self.return_type:
|
|
@@ -145,12 +206,16 @@ class AppClient:
|
|
|
145
206
|
with httpx.Client() as client:
|
|
146
207
|
retries = 100
|
|
147
208
|
for _ in range(retries):
|
|
148
|
-
|
|
209
|
+
url = info.url + "/health"
|
|
210
|
+
resp = client.get(url, timeout=60)
|
|
149
211
|
|
|
150
212
|
if resp.is_success:
|
|
151
213
|
break
|
|
152
214
|
elif resp.status_code not in (500, 404):
|
|
153
|
-
|
|
215
|
+
raise AppClientError(
|
|
216
|
+
f"Failed to GET {url}: {resp.status_code} {resp.text}",
|
|
217
|
+
status_code=resp.status_code,
|
|
218
|
+
)
|
|
154
219
|
time.sleep(0.1)
|
|
155
220
|
|
|
156
221
|
client = cls(app_cls, info.url)
|
|
@@ -191,6 +256,8 @@ class App(fal.api.BaseServable):
|
|
|
191
256
|
app_auth: ClassVar[Literal["private", "public", "shared"]] = "private"
|
|
192
257
|
request_timeout: ClassVar[int | None] = None
|
|
193
258
|
|
|
259
|
+
isolate_channel: async_grpc.Channel | None = None
|
|
260
|
+
|
|
194
261
|
def __init_subclass__(cls, **kwargs):
|
|
195
262
|
app_name = kwargs.pop("name", None) or _to_fal_app_name(cls.__name__)
|
|
196
263
|
parent_settings = getattr(cls, "host_kwargs", {})
|
|
@@ -266,11 +333,14 @@ class App(fal.api.BaseServable):
|
|
|
266
333
|
|
|
267
334
|
@app.middleware("http")
|
|
268
335
|
async def set_global_object_preference(request, call_next):
|
|
269
|
-
response = await call_next(request)
|
|
270
336
|
try:
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
337
|
+
preference_dict = get_lifecycle_preference(request) or {}
|
|
338
|
+
expiration_duration = preference_dict.get("expiration_duration_seconds")
|
|
339
|
+
if expiration_duration is not None:
|
|
340
|
+
GLOBAL_LIFECYCLE_PREFERENCE.expiration_duration_seconds = int(
|
|
341
|
+
expiration_duration
|
|
342
|
+
)
|
|
343
|
+
|
|
274
344
|
except Exception:
|
|
275
345
|
from fastapi.logger import logger
|
|
276
346
|
|
|
@@ -278,7 +348,26 @@ class App(fal.api.BaseServable):
|
|
|
278
348
|
"Failed set a global lifecycle preference %s",
|
|
279
349
|
self.__class__.__name__,
|
|
280
350
|
)
|
|
281
|
-
|
|
351
|
+
|
|
352
|
+
return await call_next(request)
|
|
353
|
+
|
|
354
|
+
@app.middleware("http")
|
|
355
|
+
async def set_request_id(request, call_next):
|
|
356
|
+
if self.isolate_channel is None:
|
|
357
|
+
grpc_port = os.environ.get("NOMAD_ALLOC_PORT_grpc")
|
|
358
|
+
self.isolate_channel = await open_isolate_channel(
|
|
359
|
+
f"localhost:{grpc_port}"
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
request_id = request.headers.get(REQUEST_ID_KEY)
|
|
363
|
+
if request_id is not None:
|
|
364
|
+
await _set_logger_labels(
|
|
365
|
+
{"fal_request_id": request_id}, channel=self.isolate_channel
|
|
366
|
+
)
|
|
367
|
+
try:
|
|
368
|
+
return await call_next(request)
|
|
369
|
+
finally:
|
|
370
|
+
await _set_logger_labels({}, channel=self.isolate_channel)
|
|
282
371
|
|
|
283
372
|
@app.exception_handler(RequestCancelledException)
|
|
284
373
|
async def value_error_exception_handler(
|
|
@@ -144,7 +144,7 @@ class File(BaseModel):
|
|
|
144
144
|
|
|
145
145
|
fdata = FileData(data, content_type, file_name)
|
|
146
146
|
|
|
147
|
-
object_lifecycle_preference =
|
|
147
|
+
object_lifecycle_preference = get_lifecycle_preference(request)
|
|
148
148
|
|
|
149
149
|
try:
|
|
150
150
|
url = repo.save(fdata, object_lifecycle_preference)
|
|
@@ -191,7 +191,7 @@ class File(BaseModel):
|
|
|
191
191
|
)
|
|
192
192
|
|
|
193
193
|
content_type = content_type or "application/octet-stream"
|
|
194
|
-
object_lifecycle_preference =
|
|
194
|
+
object_lifecycle_preference = get_lifecycle_preference(request)
|
|
195
195
|
|
|
196
196
|
try:
|
|
197
197
|
url, data = repo.save_file(
|
|
@@ -274,7 +274,7 @@ class CompressedFile(File):
|
|
|
274
274
|
shutil.rmtree(self.extract_dir)
|
|
275
275
|
|
|
276
276
|
|
|
277
|
-
def
|
|
277
|
+
def get_lifecycle_preference(request: Request) -> dict[str, str] | None:
|
|
278
278
|
import json
|
|
279
279
|
|
|
280
280
|
preference_str = (
|