fal 1.12.1__tar.gz → 1.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fal might be problematic. Click here for more details.
- {fal-1.12.1/fal.egg-info → fal-1.13.0}/PKG-INFO +1 -1
- {fal-1.12.1 → fal-1.13.0/fal.egg-info}/PKG-INFO +1 -1
- {fal-1.12.1 → fal-1.13.0}/src/fal/_fal_version.py +2 -2
- {fal-1.12.1 → fal-1.13.0}/src/fal/api.py +44 -23
- {fal-1.12.1 → fal-1.13.0}/src/fal/app.py +15 -7
- {fal-1.12.1 → fal-1.13.0}/src/fal/apps.py +42 -5
- {fal-1.12.1 → fal-1.13.0}/src/fal/auth/__init__.py +15 -31
- {fal-1.12.1 → fal-1.13.0}/src/fal/auth/auth0.py +5 -8
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/api.py +27 -8
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/auth.py +36 -15
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/profile.py +1 -1
- {fal-1.12.1 → fal-1.13.0}/src/fal/config.py +3 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/logging/__init__.py +2 -2
- {fal-1.12.1 → fal-1.13.0}/src/fal/logging/isolate.py +8 -1
- fal-1.13.0/src/fal/logging/user.py +25 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/sdk.py +28 -16
- fal-1.12.1/src/fal/logging/user.py +0 -21
- {fal-1.12.1 → fal-1.13.0}/.gitignore +0 -0
- {fal-1.12.1 → fal-1.13.0}/Makefile +0 -0
- {fal-1.12.1 → fal-1.13.0}/README.md +0 -0
- {fal-1.12.1 → fal-1.13.0}/docs/conf.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/docs/index.rst +0 -0
- {fal-1.12.1 → fal-1.13.0}/fal.egg-info/SOURCES.txt +0 -0
- {fal-1.12.1 → fal-1.13.0}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.12.1 → fal-1.13.0}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.12.1 → fal-1.13.0}/fal.egg-info/requires.txt +0 -0
- {fal-1.12.1 → fal-1.13.0}/fal.egg-info/top_level.txt +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/README.md +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.12.1 → fal-1.13.0}/openapi_rest.config.yaml +0 -0
- {fal-1.12.1 → fal-1.13.0}/pyproject.toml +0 -0
- {fal-1.12.1 → fal-1.13.0}/setup.cfg +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/__main__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/_serialization.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/_version.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/auth/local.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/_utils.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/apps.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/cli_nested_json.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/create.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/debug.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/deploy.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/doctor.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/keys.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/main.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/parser.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/run.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/runners.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/secrets.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/cli/teams.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/console/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/console/icons.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/console/ux.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/container.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/exceptions/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/exceptions/_base.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/exceptions/_cuda.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/exceptions/auth.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/files.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/flags.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/logging/style.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/logging/trace.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/py.typed +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/rest_client.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/sync.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/file/file.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/file/providers/fal.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/file/providers/s3.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/file/types.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/image/image.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/image/safety_checker.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/types.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/utils/download_utils.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/toolkit/utils/retry.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/utils.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/src/fal/workflows.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/assets/cat.png +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/cli/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/cli/test_apps.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/cli/test_auth.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/cli/test_deploy.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/cli/test_keys.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/cli/test_run.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/cli/test_secrets.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/conftest.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/integration_test.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/mainify_package/__init__.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/mainify_package/impl.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/mainify_package/utils.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/mainify_target.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/test_apps.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/test_stability.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/toolkit/file_test.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/toolkit/image_test.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/toolkit/test_types.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tests/toolkit/utils/retry.py +0 -0
- {fal-1.12.1 → fal-1.13.0}/tools/demo_script.py +0 -0
|
@@ -12,6 +12,7 @@ from functools import wraps
|
|
|
12
12
|
from os import PathLike
|
|
13
13
|
from queue import Queue
|
|
14
14
|
from typing import (
|
|
15
|
+
TYPE_CHECKING,
|
|
15
16
|
Any,
|
|
16
17
|
Callable,
|
|
17
18
|
ClassVar,
|
|
@@ -27,15 +28,11 @@ from typing import (
|
|
|
27
28
|
|
|
28
29
|
import cloudpickle
|
|
29
30
|
import grpc
|
|
30
|
-
import isolate
|
|
31
31
|
import tblib
|
|
32
32
|
import uvicorn
|
|
33
33
|
import yaml
|
|
34
34
|
from fastapi import FastAPI
|
|
35
35
|
from fastapi import __version__ as fastapi_version
|
|
36
|
-
from isolate.backends.common import active_python
|
|
37
|
-
from isolate.backends.settings import DEFAULT_SETTINGS
|
|
38
|
-
from isolate.connections import PythonIPC
|
|
39
36
|
from packaging.requirements import Requirement
|
|
40
37
|
from packaging.utils import canonicalize_name
|
|
41
38
|
from pydantic import __version__ as pydantic_version
|
|
@@ -66,6 +63,9 @@ from fal.sdk import (
|
|
|
66
63
|
get_default_credentials,
|
|
67
64
|
)
|
|
68
65
|
|
|
66
|
+
if TYPE_CHECKING:
|
|
67
|
+
from isolate.backends import BaseEnvironment
|
|
68
|
+
|
|
69
69
|
ArgsT = ParamSpec("ArgsT")
|
|
70
70
|
ReturnT = TypeVar("ReturnT", covariant=True) # noqa: PLC0105
|
|
71
71
|
|
|
@@ -83,9 +83,6 @@ SERVE_REQUIREMENTS = [
|
|
|
83
83
|
]
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
THREAD_POOL = ThreadPoolExecutor()
|
|
87
|
-
|
|
88
|
-
|
|
89
86
|
@dataclass
|
|
90
87
|
class FalServerlessError(FalServerlessException):
|
|
91
88
|
message: str
|
|
@@ -222,9 +219,10 @@ def cached(func: Callable[ArgsT, ReturnT]) -> Callable[ArgsT, ReturnT]:
|
|
|
222
219
|
) -> ReturnT:
|
|
223
220
|
from functools import lru_cache
|
|
224
221
|
|
|
225
|
-
# HACK: Using the isolate module as a global cache.
|
|
226
222
|
import isolate
|
|
227
223
|
|
|
224
|
+
# HACK: Using the isolate module as a global cache.
|
|
225
|
+
|
|
228
226
|
if not hasattr(isolate, "__cached_functions__"):
|
|
229
227
|
isolate.__cached_functions__ = {}
|
|
230
228
|
|
|
@@ -269,17 +267,23 @@ def _prepare_partial_func(
|
|
|
269
267
|
return wrapper
|
|
270
268
|
|
|
271
269
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
_AGENT_ENVIRONMENT = isolate.prepare_environment(
|
|
270
|
+
def _prepare_environment() -> BaseEnvironment:
|
|
271
|
+
import isolate
|
|
272
|
+
|
|
273
|
+
return isolate.prepare_environment(
|
|
277
274
|
"virtualenv",
|
|
278
275
|
requirements=[
|
|
279
276
|
f"cloudpickle=={cloudpickle.__version__}",
|
|
280
277
|
f"tblib=={tblib.__version__}",
|
|
281
278
|
],
|
|
282
279
|
)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@dataclass
|
|
283
|
+
class LocalHost(Host):
|
|
284
|
+
# The environment which provides the default set of
|
|
285
|
+
# packages for isolate agent to run.
|
|
286
|
+
_AGENT_ENVIRONMENT: BaseEnvironment = field(default_factory=_prepare_environment)
|
|
283
287
|
_log_printer = IsolateLogPrinter(debug=flags.DEBUG)
|
|
284
288
|
|
|
285
289
|
def run(
|
|
@@ -289,6 +293,10 @@ class LocalHost(Host):
|
|
|
289
293
|
args: tuple[Any, ...],
|
|
290
294
|
kwargs: dict[str, Any],
|
|
291
295
|
) -> ReturnT:
|
|
296
|
+
import isolate
|
|
297
|
+
from isolate.backends.settings import DEFAULT_SETTINGS
|
|
298
|
+
from isolate.connections import PythonIPC
|
|
299
|
+
|
|
292
300
|
settings = replace(
|
|
293
301
|
DEFAULT_SETTINGS,
|
|
294
302
|
serialization_method="cloudpickle",
|
|
@@ -419,8 +427,16 @@ class FalServerlessHost(Host):
|
|
|
419
427
|
|
|
420
428
|
_log_printer = IsolateLogPrinter(debug=flags.DEBUG)
|
|
421
429
|
|
|
430
|
+
_thread_pool: ThreadPoolExecutor = field(default_factory=ThreadPoolExecutor)
|
|
431
|
+
|
|
432
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
433
|
+
state = self.__dict__.copy()
|
|
434
|
+
state["_thread_pool"] = None
|
|
435
|
+
return state
|
|
436
|
+
|
|
422
437
|
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
423
438
|
self.__dict__.update(state)
|
|
439
|
+
self._thread_pool = ThreadPoolExecutor()
|
|
424
440
|
self.credentials = get_agent_credentials(self.credentials)
|
|
425
441
|
|
|
426
442
|
@property
|
|
@@ -440,6 +456,8 @@ class FalServerlessHost(Host):
|
|
|
440
456
|
deployment_strategy: Literal["recreate", "rolling"] = "recreate",
|
|
441
457
|
scale: bool = True,
|
|
442
458
|
) -> str | None:
|
|
459
|
+
from isolate.backends.common import active_python
|
|
460
|
+
|
|
443
461
|
environment_options = options.environment.copy()
|
|
444
462
|
environment_options.setdefault("python_version", active_python())
|
|
445
463
|
environments = [self._connection.define_environment(**environment_options)]
|
|
@@ -517,6 +535,8 @@ class FalServerlessHost(Host):
|
|
|
517
535
|
kwargs: dict[str, Any],
|
|
518
536
|
result_handler: Callable[..., None],
|
|
519
537
|
) -> ReturnT:
|
|
538
|
+
from isolate.backends.common import active_python
|
|
539
|
+
|
|
520
540
|
environment_options = options.environment.copy()
|
|
521
541
|
environment_options.setdefault("python_version", active_python())
|
|
522
542
|
environments = [self._connection.define_environment(**environment_options)]
|
|
@@ -611,7 +631,7 @@ class FalServerlessHost(Host):
|
|
|
611
631
|
ret.url = log.message.rsplit()[-1]
|
|
612
632
|
ret.logs.put(log)
|
|
613
633
|
|
|
614
|
-
|
|
634
|
+
self._thread_pool.submit(
|
|
615
635
|
self._run,
|
|
616
636
|
func,
|
|
617
637
|
options,
|
|
@@ -654,7 +674,6 @@ class Options:
|
|
|
654
674
|
return self.gateway.get("exposed_port")
|
|
655
675
|
|
|
656
676
|
|
|
657
|
-
_DEFAULT_HOST = FalServerlessHost()
|
|
658
677
|
_SERVE_PORT = 8080
|
|
659
678
|
|
|
660
679
|
# Overload @function to help users identify the correct signature.
|
|
@@ -704,7 +723,7 @@ def function(
|
|
|
704
723
|
python_version: str | None = None,
|
|
705
724
|
requirements: list[str] | None = None,
|
|
706
725
|
# Common options
|
|
707
|
-
host: FalServerlessHost =
|
|
726
|
+
host: FalServerlessHost | None = None,
|
|
708
727
|
serve: Literal[False] = False,
|
|
709
728
|
exposed_port: int | None = None,
|
|
710
729
|
max_concurrency: int | None = None,
|
|
@@ -733,7 +752,7 @@ def function(
|
|
|
733
752
|
python_version: str | None = None,
|
|
734
753
|
requirements: list[str] | None = None,
|
|
735
754
|
# Common options
|
|
736
|
-
host: FalServerlessHost =
|
|
755
|
+
host: FalServerlessHost | None = None,
|
|
737
756
|
serve: Literal[True],
|
|
738
757
|
exposed_port: int | None = None,
|
|
739
758
|
max_concurrency: int | None = None,
|
|
@@ -812,7 +831,7 @@ def function(
|
|
|
812
831
|
pip: list[str] | None = None,
|
|
813
832
|
channels: list[str] | None = None,
|
|
814
833
|
# Common options
|
|
815
|
-
host: FalServerlessHost =
|
|
834
|
+
host: FalServerlessHost | None = None,
|
|
816
835
|
serve: Literal[False] = False,
|
|
817
836
|
exposed_port: int | None = None,
|
|
818
837
|
max_concurrency: int | None = None,
|
|
@@ -846,7 +865,7 @@ def function(
|
|
|
846
865
|
pip: list[str] | None = None,
|
|
847
866
|
channels: list[str] | None = None,
|
|
848
867
|
# Common options
|
|
849
|
-
host: FalServerlessHost =
|
|
868
|
+
host: FalServerlessHost | None = None,
|
|
850
869
|
serve: Literal[True],
|
|
851
870
|
exposed_port: int | None = None,
|
|
852
871
|
max_concurrency: int | None = None,
|
|
@@ -874,7 +893,7 @@ def function(
|
|
|
874
893
|
*,
|
|
875
894
|
image: ContainerImage | None = None,
|
|
876
895
|
# Common options
|
|
877
|
-
host: FalServerlessHost =
|
|
896
|
+
host: FalServerlessHost | None = None,
|
|
878
897
|
serve: Literal[False] = False,
|
|
879
898
|
exposed_port: int | None = None,
|
|
880
899
|
max_concurrency: int | None = None,
|
|
@@ -902,7 +921,7 @@ def function(
|
|
|
902
921
|
*,
|
|
903
922
|
image: ContainerImage | None = None,
|
|
904
923
|
# Common options
|
|
905
|
-
host: FalServerlessHost =
|
|
924
|
+
host: FalServerlessHost | None = None,
|
|
906
925
|
serve: Literal[True],
|
|
907
926
|
exposed_port: int | None = None,
|
|
908
927
|
max_concurrency: int | None = None,
|
|
@@ -928,15 +947,17 @@ def function(
|
|
|
928
947
|
def function( # type: ignore
|
|
929
948
|
kind: str = "virtualenv",
|
|
930
949
|
*,
|
|
931
|
-
host: Host =
|
|
950
|
+
host: Host | None = None,
|
|
932
951
|
**config: Any,
|
|
933
952
|
):
|
|
953
|
+
if host is None:
|
|
954
|
+
host = FalServerlessHost()
|
|
934
955
|
options = host.parse_options(kind=kind, **config)
|
|
935
956
|
|
|
936
957
|
def wrapper(func: Callable[ArgsT, ReturnT]):
|
|
937
958
|
include_modules_from(func)
|
|
938
959
|
proxy = IsolatedFunction(
|
|
939
|
-
host=host,
|
|
960
|
+
host=host, # type: ignore
|
|
940
961
|
raw_func=func, # type: ignore
|
|
941
962
|
options=options,
|
|
942
963
|
)
|
|
@@ -16,11 +16,17 @@ from typing import Any, Callable, ClassVar, Literal, TypeVar
|
|
|
16
16
|
import fastapi
|
|
17
17
|
import grpc.aio as async_grpc
|
|
18
18
|
import httpx
|
|
19
|
-
from isolate.server import definitions
|
|
20
19
|
|
|
21
|
-
import fal.api
|
|
22
20
|
from fal._serialization import include_modules_from
|
|
23
|
-
from fal.api import
|
|
21
|
+
from fal.api import (
|
|
22
|
+
SERVE_REQUIREMENTS,
|
|
23
|
+
BaseServable,
|
|
24
|
+
IsolatedFunction,
|
|
25
|
+
RouteSignature,
|
|
26
|
+
)
|
|
27
|
+
from fal.api import (
|
|
28
|
+
function as fal_function,
|
|
29
|
+
)
|
|
24
30
|
from fal.exceptions import FalServerlessException, RequestCancelledException
|
|
25
31
|
from fal.logging import get_logger
|
|
26
32
|
from fal.toolkit.file import request_lifecycle_preference
|
|
@@ -70,6 +76,8 @@ async def _set_logger_labels(
|
|
|
70
76
|
try:
|
|
71
77
|
import sys
|
|
72
78
|
|
|
79
|
+
from isolate.server import definitions
|
|
80
|
+
|
|
73
81
|
# Flush any prints that were buffered before setting the logger labels
|
|
74
82
|
sys.stderr.flush()
|
|
75
83
|
sys.stdout.flush()
|
|
@@ -89,7 +97,7 @@ async def _set_logger_labels(
|
|
|
89
97
|
pass
|
|
90
98
|
|
|
91
99
|
|
|
92
|
-
def wrap_app(cls: type[App], **kwargs) ->
|
|
100
|
+
def wrap_app(cls: type[App], **kwargs) -> IsolatedFunction:
|
|
93
101
|
include_modules_from(cls)
|
|
94
102
|
|
|
95
103
|
def initialize_and_serve():
|
|
@@ -111,7 +119,7 @@ def wrap_app(cls: type[App], **kwargs) -> fal.api.IsolatedFunction:
|
|
|
111
119
|
if kind == "container":
|
|
112
120
|
cls.host_kwargs.pop("resolver", None)
|
|
113
121
|
|
|
114
|
-
wrapper =
|
|
122
|
+
wrapper = fal_function(
|
|
115
123
|
kind,
|
|
116
124
|
requirements=cls.requirements,
|
|
117
125
|
machine_type=cls.machine_type,
|
|
@@ -123,7 +131,7 @@ def wrap_app(cls: type[App], **kwargs) -> fal.api.IsolatedFunction:
|
|
|
123
131
|
serve=False,
|
|
124
132
|
)
|
|
125
133
|
fn = wrapper(initialize_and_serve)
|
|
126
|
-
fn.options.add_requirements(
|
|
134
|
+
fn.options.add_requirements(SERVE_REQUIREMENTS)
|
|
127
135
|
if realtime_app:
|
|
128
136
|
fn.options.add_requirements(REALTIME_APP_REQUIREMENTS)
|
|
129
137
|
|
|
@@ -255,7 +263,7 @@ def _print_python_packages() -> None:
|
|
|
255
263
|
print("[debug] Python packages installed:", ", ".join(packages))
|
|
256
264
|
|
|
257
265
|
|
|
258
|
-
class App(
|
|
266
|
+
class App(BaseServable):
|
|
259
267
|
requirements: ClassVar[list[str]] = []
|
|
260
268
|
machine_type: ClassVar[str] = "S"
|
|
261
269
|
num_gpus: ClassVar[int | None] = None
|
|
@@ -4,6 +4,7 @@ import json
|
|
|
4
4
|
import time
|
|
5
5
|
from contextlib import contextmanager
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
|
+
from functools import lru_cache
|
|
7
8
|
from typing import TYPE_CHECKING, Any, Iterator
|
|
8
9
|
|
|
9
10
|
import httpx
|
|
@@ -14,6 +15,7 @@ from fal.sdk import Credentials, get_default_credentials
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
16
|
from websockets.sync.connection import Connection
|
|
16
17
|
|
|
18
|
+
_STREAM_URL_FORMAT = f"https://{flags.FAL_RUN_HOST}/{{app_id}}"
|
|
17
19
|
_QUEUE_URL_FORMAT = f"https://queue.{flags.FAL_RUN_HOST}/{{app_id}}"
|
|
18
20
|
_REALTIME_URL_FORMAT = f"wss://{flags.FAL_RUN_HOST}/{{app_id}}"
|
|
19
21
|
_WS_URL_FORMAT = f"wss://ws.{flags.FAL_RUN_HOST}/{{app_id}}"
|
|
@@ -55,6 +57,11 @@ class Completed(_Status):
|
|
|
55
57
|
logs: list[dict[str, Any]] | None = field()
|
|
56
58
|
|
|
57
59
|
|
|
60
|
+
@lru_cache(maxsize=1)
|
|
61
|
+
def _get_http_client() -> httpx.Client:
|
|
62
|
+
return httpx.Client(headers={"User-Agent": "Fal/Python"})
|
|
63
|
+
|
|
64
|
+
|
|
58
65
|
@dataclass
|
|
59
66
|
class RequestHandle:
|
|
60
67
|
"""A handle to an async inference request."""
|
|
@@ -62,6 +69,8 @@ class RequestHandle:
|
|
|
62
69
|
app_id: str
|
|
63
70
|
request_id: str
|
|
64
71
|
|
|
72
|
+
_client: httpx.Client = field(default_factory=_get_http_client)
|
|
73
|
+
|
|
65
74
|
# Use the credentials that were used to submit the request by default.
|
|
66
75
|
_creds: Credentials = field(default_factory=get_default_credentials, repr=False)
|
|
67
76
|
|
|
@@ -82,7 +91,7 @@ class RequestHandle:
|
|
|
82
91
|
_QUEUE_URL_FORMAT.format(app_id=self.app_id)
|
|
83
92
|
+ f"/requests/{self.request_id}/status/"
|
|
84
93
|
)
|
|
85
|
-
response =
|
|
94
|
+
response = self._client.get(
|
|
86
95
|
url,
|
|
87
96
|
headers=self._creds.to_headers(),
|
|
88
97
|
params={"logs": int(logs)},
|
|
@@ -107,7 +116,7 @@ class RequestHandle:
|
|
|
107
116
|
_QUEUE_URL_FORMAT.format(app_id=self.app_id)
|
|
108
117
|
+ f"/requests/{self.request_id}/cancel"
|
|
109
118
|
)
|
|
110
|
-
response =
|
|
119
|
+
response = self._client.put(url, headers=self._creds.to_headers())
|
|
111
120
|
response.raise_for_status()
|
|
112
121
|
|
|
113
122
|
def iter_events(
|
|
@@ -134,7 +143,7 @@ class RequestHandle:
|
|
|
134
143
|
_QUEUE_URL_FORMAT.format(app_id=self.app_id)
|
|
135
144
|
+ f"/requests/{self.request_id}/"
|
|
136
145
|
)
|
|
137
|
-
response =
|
|
146
|
+
response = self._client.get(url, headers=self._creds.to_headers())
|
|
138
147
|
try:
|
|
139
148
|
response.raise_for_status()
|
|
140
149
|
except httpx.HTTPStatusError as e:
|
|
@@ -159,7 +168,33 @@ class RequestHandle:
|
|
|
159
168
|
return self.fetch_result()
|
|
160
169
|
|
|
161
170
|
|
|
162
|
-
|
|
171
|
+
def stream(
|
|
172
|
+
app_id: str, arguments: dict[str, Any], *, path: str = ""
|
|
173
|
+
) -> Iterator[str | bytes]:
|
|
174
|
+
"""Stream an inference task on a Fal app."""
|
|
175
|
+
|
|
176
|
+
app_id = _backwards_compatible_app_id(app_id)
|
|
177
|
+
url = _STREAM_URL_FORMAT.format(app_id=app_id)
|
|
178
|
+
if path:
|
|
179
|
+
_path = path[len("/") :] if path.startswith("/") else path
|
|
180
|
+
url += "/" + _path
|
|
181
|
+
|
|
182
|
+
creds = get_default_credentials()
|
|
183
|
+
client = _get_http_client()
|
|
184
|
+
|
|
185
|
+
response = client.post(
|
|
186
|
+
url,
|
|
187
|
+
json=arguments,
|
|
188
|
+
headers=creds.to_headers(),
|
|
189
|
+
)
|
|
190
|
+
response.raise_for_status()
|
|
191
|
+
|
|
192
|
+
if response.headers["Content-Type"].startswith("text/event-stream"):
|
|
193
|
+
for line in response.iter_lines():
|
|
194
|
+
if line:
|
|
195
|
+
yield line
|
|
196
|
+
else:
|
|
197
|
+
yield from response.iter_bytes()
|
|
163
198
|
|
|
164
199
|
|
|
165
200
|
def run(app_id: str, arguments: dict[str, Any], *, path: str = "") -> dict[str, Any]:
|
|
@@ -181,8 +216,9 @@ def submit(app_id: str, arguments: dict[str, Any], *, path: str = "") -> Request
|
|
|
181
216
|
url += "/" + _path
|
|
182
217
|
|
|
183
218
|
creds = get_default_credentials()
|
|
219
|
+
client = _get_http_client()
|
|
184
220
|
|
|
185
|
-
response =
|
|
221
|
+
response = client.post(
|
|
186
222
|
url,
|
|
187
223
|
json=arguments,
|
|
188
224
|
headers=creds.to_headers(),
|
|
@@ -194,6 +230,7 @@ def submit(app_id: str, arguments: dict[str, Any], *, path: str = "") -> Request
|
|
|
194
230
|
app_id=app_id,
|
|
195
231
|
request_id=data["request_id"],
|
|
196
232
|
_creds=creds,
|
|
233
|
+
_client=client,
|
|
197
234
|
)
|
|
198
235
|
|
|
199
236
|
|
|
@@ -7,8 +7,6 @@ from typing import Optional
|
|
|
7
7
|
|
|
8
8
|
from fal.auth import auth0, local
|
|
9
9
|
from fal.config import Config
|
|
10
|
-
from fal.console import console
|
|
11
|
-
from fal.console.icons import CHECK_ICON
|
|
12
10
|
from fal.exceptions import FalServerlessException
|
|
13
11
|
from fal.exceptions.auth import UnauthenticatedException
|
|
14
12
|
|
|
@@ -73,26 +71,6 @@ def key_credentials() -> tuple[str, str] | None:
|
|
|
73
71
|
return None
|
|
74
72
|
|
|
75
73
|
|
|
76
|
-
def login():
|
|
77
|
-
token_data = auth0.login()
|
|
78
|
-
with local.lock_token():
|
|
79
|
-
local.save_token(token_data["refresh_token"])
|
|
80
|
-
|
|
81
|
-
USER.invalidate()
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def logout():
|
|
85
|
-
refresh_token, _ = local.load_token()
|
|
86
|
-
if refresh_token is None:
|
|
87
|
-
raise FalServerlessException("You're not logged in")
|
|
88
|
-
auth0.revoke(refresh_token)
|
|
89
|
-
with local.lock_token():
|
|
90
|
-
local.delete_token()
|
|
91
|
-
|
|
92
|
-
USER.invalidate()
|
|
93
|
-
console.print(f"{CHECK_ICON} Logged out of [cyan bold]fal[/]. Bye!")
|
|
94
|
-
|
|
95
|
-
|
|
96
74
|
def _fetch_access_token() -> str:
|
|
97
75
|
"""
|
|
98
76
|
Load the refresh token, request a new access_token (refreshing the refresh token)
|
|
@@ -147,6 +125,21 @@ def _fetch_teams(bearer_token: str) -> list[dict]:
|
|
|
147
125
|
raise FalServerlessException("Failed to fetch teams") from exc
|
|
148
126
|
|
|
149
127
|
|
|
128
|
+
def login(console):
|
|
129
|
+
token_data = auth0.login(console)
|
|
130
|
+
with local.lock_token():
|
|
131
|
+
local.save_token(token_data["refresh_token"])
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def logout(console):
|
|
135
|
+
refresh_token, _ = local.load_token()
|
|
136
|
+
if refresh_token is None:
|
|
137
|
+
raise FalServerlessException("You're not logged in")
|
|
138
|
+
auth0.revoke(refresh_token, console)
|
|
139
|
+
with local.lock_token():
|
|
140
|
+
local.delete_token()
|
|
141
|
+
|
|
142
|
+
|
|
150
143
|
@dataclass
|
|
151
144
|
class UserAccess:
|
|
152
145
|
_access_token: str | None = field(repr=False, default=None)
|
|
@@ -154,12 +147,6 @@ class UserAccess:
|
|
|
154
147
|
_exc: Exception | None = field(repr=False, default=None)
|
|
155
148
|
_accounts: list[dict] | None = field(repr=False, default=None)
|
|
156
149
|
|
|
157
|
-
def invalidate(self) -> None:
|
|
158
|
-
self._access_token = None
|
|
159
|
-
self._user_info = None
|
|
160
|
-
self._exc = None
|
|
161
|
-
self._accounts = None
|
|
162
|
-
|
|
163
150
|
@property
|
|
164
151
|
def info(self) -> dict:
|
|
165
152
|
if self._user_info is None:
|
|
@@ -203,6 +190,3 @@ class UserAccess:
|
|
|
203
190
|
if t["nickname"].lower() == team.lower():
|
|
204
191
|
return t
|
|
205
192
|
raise ValueError(f"Team {team} not found")
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
USER = UserAccess()
|
|
@@ -6,8 +6,6 @@ import warnings
|
|
|
6
6
|
|
|
7
7
|
import httpx
|
|
8
8
|
|
|
9
|
-
from fal.console import console
|
|
10
|
-
from fal.console.icons import CHECK_ICON
|
|
11
9
|
from fal.console.ux import maybe_open_browser_tab
|
|
12
10
|
from fal.exceptions import FalServerlessException
|
|
13
11
|
|
|
@@ -26,7 +24,7 @@ def logout_url(return_url: str):
|
|
|
26
24
|
return f"https://{AUTH0_DOMAIN}/v2/logout?client_id={AUTH0_CLIENT_ID}&returnTo={return_url}"
|
|
27
25
|
|
|
28
26
|
|
|
29
|
-
def _open_browser(url: str, code: str | None) -> None:
|
|
27
|
+
def _open_browser(url: str, code: str | None, console) -> None:
|
|
30
28
|
maybe_open_browser_tab(url)
|
|
31
29
|
|
|
32
30
|
console.print(
|
|
@@ -41,7 +39,7 @@ def _open_browser(url: str, code: str | None) -> None:
|
|
|
41
39
|
)
|
|
42
40
|
|
|
43
41
|
|
|
44
|
-
def login() -> dict:
|
|
42
|
+
def login(console) -> dict:
|
|
45
43
|
"""
|
|
46
44
|
Runs the device authorization flow and stores the user object in memory
|
|
47
45
|
"""
|
|
@@ -63,7 +61,7 @@ def login() -> dict:
|
|
|
63
61
|
|
|
64
62
|
url = logout_url(device_confirmation_url)
|
|
65
63
|
|
|
66
|
-
_open_browser(url, device_user_code)
|
|
64
|
+
_open_browser(url, device_user_code, console)
|
|
67
65
|
|
|
68
66
|
# This is needed to suppress the ResourceWarning emitted
|
|
69
67
|
# when the process is waiting for user confirmation
|
|
@@ -84,7 +82,6 @@ def login() -> dict:
|
|
|
84
82
|
token_data = token_response.json()
|
|
85
83
|
if token_response.status_code == 200:
|
|
86
84
|
status.update(spinner=None)
|
|
87
|
-
console.print(f"{CHECK_ICON} Authenticated successfully, welcome!")
|
|
88
85
|
|
|
89
86
|
validate_id_token(token_data["id_token"])
|
|
90
87
|
|
|
@@ -118,7 +115,7 @@ def refresh(token: str) -> dict:
|
|
|
118
115
|
raise FalServerlessException(token_data["error_description"])
|
|
119
116
|
|
|
120
117
|
|
|
121
|
-
def revoke(token: str):
|
|
118
|
+
def revoke(token: str, console):
|
|
122
119
|
token_payload = {
|
|
123
120
|
"client_id": AUTH0_CLIENT_ID,
|
|
124
121
|
"token": token,
|
|
@@ -132,7 +129,7 @@ def revoke(token: str):
|
|
|
132
129
|
token_data = token_response.json()
|
|
133
130
|
raise FalServerlessException(token_data["error_description"])
|
|
134
131
|
|
|
135
|
-
_open_browser(logout_url(WEBSITE_URL), None)
|
|
132
|
+
_open_browser(logout_url(WEBSITE_URL), None, console)
|
|
136
133
|
|
|
137
134
|
|
|
138
135
|
def get_user_info(bearer_token: str) -> dict:
|
|
@@ -10,19 +10,38 @@ KV_SPLIT_RE = re.compile(r"(=|:=)")
|
|
|
10
10
|
|
|
11
11
|
def _api(args):
|
|
12
12
|
"""Handle the api command execution."""
|
|
13
|
-
from rich.console import Group
|
|
14
|
-
from rich.live import Live
|
|
15
|
-
from rich.panel import Panel
|
|
16
|
-
from rich.text import Text
|
|
17
|
-
|
|
18
13
|
from . import cli_nested_json
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
params_split = [KV_SPLIT_RE.split(param) for param in args.params]
|
|
21
16
|
params = cli_nested_json.interpret_nested_json( # type: ignore
|
|
22
|
-
[(key, value) for key, _, value in
|
|
17
|
+
[(key, value) for key, _, value in params_split]
|
|
23
18
|
)
|
|
24
19
|
|
|
25
|
-
|
|
20
|
+
if args.model_id.endswith("/stream"):
|
|
21
|
+
stream_run(args.model_id, params)
|
|
22
|
+
else:
|
|
23
|
+
queue_run(args.model_id, params)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def stream_run(model_id: str, params: dict):
|
|
27
|
+
res = fal.apps.stream(model_id, params) # type: ignore
|
|
28
|
+
for line in res:
|
|
29
|
+
if isinstance(line, str):
|
|
30
|
+
rich.print(line)
|
|
31
|
+
else:
|
|
32
|
+
if isinstance(line, memoryview):
|
|
33
|
+
rich.print(line.tobytes().decode())
|
|
34
|
+
else:
|
|
35
|
+
rich.print(line.decode())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def queue_run(model_id: str, params: dict):
|
|
39
|
+
from rich.console import Group
|
|
40
|
+
from rich.live import Live
|
|
41
|
+
from rich.panel import Panel
|
|
42
|
+
from rich.text import Text
|
|
43
|
+
|
|
44
|
+
handle = fal.apps.submit(model_id, params) # type: ignore
|
|
26
45
|
logs = [] # type: ignore
|
|
27
46
|
|
|
28
47
|
with Live(auto_refresh=False) as live:
|