fal 1.25.0__tar.gz → 1.26.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fal might be problematic. Click here for more details.
- {fal-1.25.0/fal.egg-info → fal-1.26.1}/PKG-INFO +1 -1
- {fal-1.25.0 → fal-1.26.1/fal.egg-info}/PKG-INFO +1 -1
- {fal-1.25.0 → fal-1.26.1}/src/fal/_fal_version.py +2 -2
- {fal-1.25.0 → fal-1.26.1}/src/fal/api.py +8 -6
- {fal-1.25.0 → fal-1.26.1}/src/fal/app.py +50 -13
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/apps.py +6 -2
- {fal-1.25.0 → fal-1.26.1}/src/fal/files.py +96 -6
- {fal-1.25.0 → fal-1.26.1}/src/fal/sdk.py +25 -14
- {fal-1.25.0 → fal-1.26.1}/tests/test_apps.py +16 -19
- {fal-1.25.0 → fal-1.26.1}/.gitignore +0 -0
- {fal-1.25.0 → fal-1.26.1}/Makefile +0 -0
- {fal-1.25.0 → fal-1.26.1}/README.md +0 -0
- {fal-1.25.0 → fal-1.26.1}/docs/conf.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/docs/index.rst +0 -0
- {fal-1.25.0 → fal-1.26.1}/fal.egg-info/SOURCES.txt +0 -0
- {fal-1.25.0 → fal-1.26.1}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.25.0 → fal-1.26.1}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.25.0 → fal-1.26.1}/fal.egg-info/requires.txt +0 -0
- {fal-1.25.0 → fal-1.26.1}/fal.egg-info/top_level.txt +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/README.md +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.25.0 → fal-1.26.1}/openapi_rest.config.yaml +0 -0
- {fal-1.25.0 → fal-1.26.1}/pyproject.toml +0 -0
- {fal-1.25.0 → fal-1.26.1}/setup.cfg +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/__main__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/_serialization.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/_version.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/apps.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/auth/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/auth/auth0.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/auth/local.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/_utils.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/api.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/auth.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/cli_nested_json.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/create.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/debug.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/deploy.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/doctor.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/files.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/keys.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/main.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/parser.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/profile.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/run.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/runners.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/secrets.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/cli/teams.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/config.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/console/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/console/icons.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/console/ux.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/container.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/exceptions/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/exceptions/_base.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/exceptions/_cuda.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/exceptions/auth.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/flags.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/logging/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/logging/isolate.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/logging/style.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/logging/trace.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/logging/user.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/project.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/py.typed +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/rest_client.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/sync.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/audio/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/audio/audio.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/file/file.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/file/providers/fal.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/file/providers/s3.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/file/types.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/image/image.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/image/safety_checker.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/kv.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/types.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/utils/download_utils.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/utils/endpoint.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/utils/retry.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/video/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/toolkit/video/video.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/utils.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/src/fal/workflows.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/assets/cat.png +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/cli/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/cli/test_apps.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/cli/test_auth.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/cli/test_deploy.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/cli/test_keys.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/cli/test_run.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/cli/test_secrets.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/conftest.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/integration_test.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/mainify_package/__init__.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/mainify_package/impl.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/mainify_package/utils.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/mainify_target.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/test_kv.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/test_stability.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/toolkit/file/providers/test_fal_retry.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/toolkit/file_test.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/toolkit/image_test.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/toolkit/test_types.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tests/toolkit/utils/retry.py +0 -0
- {fal-1.25.0 → fal-1.26.1}/tools/demo_script.py +0 -0
|
@@ -319,24 +319,26 @@ def _handle_grpc_error():
|
|
|
319
319
|
try:
|
|
320
320
|
return fn(*args, **kwargs)
|
|
321
321
|
except grpc.RpcError as e:
|
|
322
|
+
msg = e.details() or str(e)
|
|
322
323
|
if e.code() == grpc.StatusCode.UNAVAILABLE:
|
|
323
324
|
raise FalServerlessError(
|
|
324
325
|
"Could not reach fal host. "
|
|
325
326
|
"This is most likely a transient problem. "
|
|
326
|
-
"
|
|
327
|
+
"If it persists, please reach out to support@fal.ai with the following details: " # noqa: E501
|
|
328
|
+
f"{msg}"
|
|
327
329
|
)
|
|
328
|
-
elif
|
|
330
|
+
elif msg.endswith("died with <Signals.SIGKILL: 9>.`."):
|
|
329
331
|
raise FalServerlessError(
|
|
330
332
|
"Isolated function crashed. "
|
|
331
333
|
"This is likely due to resource overflow. "
|
|
332
334
|
"You can try again by setting a bigger `machine_type`"
|
|
333
335
|
)
|
|
334
336
|
elif e.code() == grpc.StatusCode.INVALID_ARGUMENT and (
|
|
335
|
-
"The function function could not be deserialized" in
|
|
337
|
+
"The function function could not be deserialized" in msg
|
|
336
338
|
):
|
|
337
|
-
raise FalMissingDependencyError(
|
|
339
|
+
raise FalMissingDependencyError(msg) from None
|
|
338
340
|
else:
|
|
339
|
-
raise FalServerlessError(
|
|
341
|
+
raise FalServerlessError(msg)
|
|
340
342
|
|
|
341
343
|
return handler
|
|
342
344
|
|
|
@@ -498,7 +500,7 @@ class FalServerlessHost(Host):
|
|
|
498
500
|
partial_func,
|
|
499
501
|
environments,
|
|
500
502
|
application_name=application_name,
|
|
501
|
-
|
|
503
|
+
auth_mode=application_auth_mode,
|
|
502
504
|
machine_requirements=machine_requirements,
|
|
503
505
|
metadata=metadata,
|
|
504
506
|
deployment_strategy=deployment_strategy,
|
|
@@ -197,7 +197,14 @@ class AppClient:
|
|
|
197
197
|
|
|
198
198
|
@classmethod
|
|
199
199
|
@contextmanager
|
|
200
|
-
def connect(
|
|
200
|
+
def connect(
|
|
201
|
+
cls,
|
|
202
|
+
app_cls,
|
|
203
|
+
*,
|
|
204
|
+
health_request_timeout: int = 30,
|
|
205
|
+
startup_timeout: int = 60,
|
|
206
|
+
health_check_interval: float = 0.5,
|
|
207
|
+
):
|
|
201
208
|
app = wrap_app(app_cls)
|
|
202
209
|
info = app.spawn()
|
|
203
210
|
_shutdown_event = threading.Event()
|
|
@@ -214,20 +221,50 @@ class AppClient:
|
|
|
214
221
|
_log_printer.start()
|
|
215
222
|
|
|
216
223
|
try:
|
|
224
|
+
if info.url is None:
|
|
225
|
+
raise AppClientError(
|
|
226
|
+
"App spawn failed: no URL returned",
|
|
227
|
+
status_code=500,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
start_time = time.perf_counter()
|
|
231
|
+
url = info.url + "/health"
|
|
232
|
+
last_error = None
|
|
233
|
+
attempt = 0
|
|
234
|
+
|
|
217
235
|
with httpx.Client() as client:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
raise AppClientError(
|
|
227
|
-
f"Failed to GET {url}: {resp.status_code} {resp.text}",
|
|
228
|
-
status_code=resp.status_code,
|
|
236
|
+
while time.perf_counter() - start_time < startup_timeout:
|
|
237
|
+
attempt += 1
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
resp = client.get(url, timeout=health_request_timeout)
|
|
241
|
+
except httpx.TimeoutException:
|
|
242
|
+
last_error = (
|
|
243
|
+
f"Request timed out after {health_request_timeout} seconds"
|
|
229
244
|
)
|
|
230
|
-
|
|
245
|
+
except httpx.TransportError as e:
|
|
246
|
+
last_error = f"Network error: {e}"
|
|
247
|
+
else:
|
|
248
|
+
if resp.is_success:
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
if resp.status_code in (500, 404):
|
|
252
|
+
last_error = f"Server not ready (HTTP {resp.status_code})"
|
|
253
|
+
else:
|
|
254
|
+
raise AppClientError(
|
|
255
|
+
"Health check failed with non-retryable error: "
|
|
256
|
+
f"{resp.status_code} {resp.text}",
|
|
257
|
+
status_code=resp.status_code,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
time.sleep(health_check_interval)
|
|
261
|
+
else:
|
|
262
|
+
# retry loop completed without success
|
|
263
|
+
raise AppClientError(
|
|
264
|
+
f"Health check failed after {startup_timeout}s "
|
|
265
|
+
f"({attempt} attempts). Last error: {last_error}",
|
|
266
|
+
status_code=500,
|
|
267
|
+
)
|
|
231
268
|
|
|
232
269
|
client = cls(app_cls, info.url)
|
|
233
270
|
yield client
|
|
@@ -270,7 +270,7 @@ def _add_set_rev_parser(subparsers, parents):
|
|
|
270
270
|
parser.add_argument(
|
|
271
271
|
"--auth",
|
|
272
272
|
choices=ALIAS_AUTH_MODES,
|
|
273
|
-
default=
|
|
273
|
+
default=None,
|
|
274
274
|
help="Application authentication mode.",
|
|
275
275
|
)
|
|
276
276
|
parser.set_defaults(func=_set_rev)
|
|
@@ -310,7 +310,11 @@ def _add_runners_parser(subparsers, parents):
|
|
|
310
310
|
def _delete(args):
|
|
311
311
|
client = get_client(args.host, args.team)
|
|
312
312
|
with client.connect() as connection:
|
|
313
|
-
connection.delete_alias(args.app_name)
|
|
313
|
+
res = connection.delete_alias(args.app_name)
|
|
314
|
+
if res is None:
|
|
315
|
+
args.console.print(f"Application {args.app_name!r} not found.")
|
|
316
|
+
else:
|
|
317
|
+
args.console.print(f"Application {args.app_name!r} deleted ({res})")
|
|
314
318
|
|
|
315
319
|
|
|
316
320
|
def _add_delete_parser(subparsers, parents):
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import concurrent.futures
|
|
2
|
+
import math
|
|
1
3
|
import os
|
|
2
4
|
import posixpath
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
3
6
|
from functools import cached_property
|
|
4
7
|
from typing import TYPE_CHECKING
|
|
5
8
|
|
|
@@ -9,6 +12,19 @@ if TYPE_CHECKING:
|
|
|
9
12
|
import httpx
|
|
10
13
|
|
|
11
14
|
USER_AGENT = "fal-sdk/1.14.0 (python)"
|
|
15
|
+
MULTIPART_THRESHOLD = 10 * 1024 * 1024 # 10MB
|
|
16
|
+
MULTIPART_CHUNK_SIZE = 10 * 1024 * 1024 # 10MB
|
|
17
|
+
MULTIPART_WORKERS = 2 # only 2 because our REST is currently struggling with more
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _compute_md5(lpath, chunk_size=8192):
|
|
21
|
+
import hashlib
|
|
22
|
+
|
|
23
|
+
hasher = hashlib.md5()
|
|
24
|
+
with open(lpath, "rb") as fobj:
|
|
25
|
+
for chunk in iter(lambda: fobj.read(chunk_size), b""):
|
|
26
|
+
hasher.update(chunk)
|
|
27
|
+
return hasher.hexdigest()
|
|
12
28
|
|
|
13
29
|
|
|
14
30
|
class FalFileSystem(AbstractFileSystem):
|
|
@@ -95,16 +111,90 @@ class FalFileSystem(AbstractFileSystem):
|
|
|
95
111
|
response = self._request("GET", f"/files/file/{rpath}")
|
|
96
112
|
fobj.write(response.content)
|
|
97
113
|
|
|
114
|
+
def _put_file_part(self, rpath, lpath, upload_id, part_number, chunk_size):
|
|
115
|
+
offset = (part_number - 1) * chunk_size
|
|
116
|
+
with open(lpath, "rb") as fobj:
|
|
117
|
+
fobj.seek(offset)
|
|
118
|
+
chunk = fobj.read(chunk_size)
|
|
119
|
+
response = self._request(
|
|
120
|
+
"PUT",
|
|
121
|
+
f"/files/file/multipart/{rpath}/{upload_id}/{part_number}",
|
|
122
|
+
files={"file_upload": (posixpath.basename(lpath), chunk)},
|
|
123
|
+
)
|
|
124
|
+
data = response.json()
|
|
125
|
+
return {
|
|
126
|
+
"part_number": data["part_number"],
|
|
127
|
+
"etag": data["etag"],
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
def _put_file_multipart(self, lpath, rpath, size, progress):
|
|
131
|
+
response = self._request(
|
|
132
|
+
"POST",
|
|
133
|
+
f"/files/file/multipart/{rpath}/initiate",
|
|
134
|
+
)
|
|
135
|
+
upload_id = response.json()["upload_id"]
|
|
136
|
+
|
|
137
|
+
parts = []
|
|
138
|
+
num_parts = math.ceil(size / MULTIPART_CHUNK_SIZE)
|
|
139
|
+
md5 = _compute_md5(lpath)
|
|
140
|
+
|
|
141
|
+
task = progress.add_task(f"{os.path.basename(lpath)}", total=num_parts)
|
|
142
|
+
|
|
143
|
+
with ThreadPoolExecutor(max_workers=MULTIPART_WORKERS) as executor:
|
|
144
|
+
futures = []
|
|
145
|
+
|
|
146
|
+
for part_number in range(1, num_parts + 1):
|
|
147
|
+
futures.append(
|
|
148
|
+
executor.submit(
|
|
149
|
+
self._put_file_part,
|
|
150
|
+
rpath,
|
|
151
|
+
lpath,
|
|
152
|
+
upload_id,
|
|
153
|
+
part_number,
|
|
154
|
+
MULTIPART_CHUNK_SIZE,
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
for future in concurrent.futures.as_completed(futures):
|
|
159
|
+
parts.append(future.result())
|
|
160
|
+
progress.advance(task)
|
|
161
|
+
|
|
162
|
+
response = self._request(
|
|
163
|
+
"POST",
|
|
164
|
+
f"/files/file/multipart/{rpath}/{upload_id}/complete",
|
|
165
|
+
json={"parts": parts},
|
|
166
|
+
)
|
|
167
|
+
data = response.json()
|
|
168
|
+
if data["etag"] != md5:
|
|
169
|
+
raise RuntimeError(
|
|
170
|
+
f"MD5 mismatch on {rpath}: {data['etag']} != {md5}, "
|
|
171
|
+
"please contact support"
|
|
172
|
+
)
|
|
173
|
+
|
|
98
174
|
def put_file(self, lpath, rpath, mode="overwrite", **kwargs):
|
|
175
|
+
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
|
|
176
|
+
|
|
99
177
|
if os.path.isdir(lpath):
|
|
100
178
|
return
|
|
101
179
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
180
|
+
size = os.path.getsize(lpath)
|
|
181
|
+
with Progress(
|
|
182
|
+
SpinnerColumn(),
|
|
183
|
+
TextColumn("[progress.description]{task.description}"),
|
|
184
|
+
BarColumn(),
|
|
185
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
186
|
+
) as progress:
|
|
187
|
+
if size > MULTIPART_THRESHOLD:
|
|
188
|
+
self._put_file_multipart(lpath, rpath, size, progress)
|
|
189
|
+
else:
|
|
190
|
+
task = progress.add_task(f"{os.path.basename(lpath)}", total=1)
|
|
191
|
+
with open(lpath, "rb") as fobj:
|
|
192
|
+
self._request(
|
|
193
|
+
"POST",
|
|
194
|
+
f"/files/file/local/{rpath}",
|
|
195
|
+
files={"file_upload": (posixpath.basename(lpath), fobj)},
|
|
196
|
+
)
|
|
197
|
+
progress.advance(task)
|
|
108
198
|
self.dircache.clear()
|
|
109
199
|
|
|
110
200
|
def put_file_from_url(self, url, rpath, mode="overwrite", **kwargs):
|
|
@@ -45,6 +45,9 @@ logger = get_logger(__name__)
|
|
|
45
45
|
patch_pickle()
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
AuthMode = Optional[Literal["public", "private", "shared"]]
|
|
49
|
+
|
|
50
|
+
|
|
48
51
|
class ServerCredentials:
|
|
49
52
|
def to_grpc(self) -> grpc.ChannelCredentials:
|
|
50
53
|
raise NotImplementedError
|
|
@@ -565,7 +568,7 @@ class FalServerlessConnection:
|
|
|
565
568
|
function: Callable[..., ResultT],
|
|
566
569
|
environments: list[isolate_proto.EnvironmentDefinition],
|
|
567
570
|
application_name: str | None = None,
|
|
568
|
-
|
|
571
|
+
auth_mode: AuthMode = None,
|
|
569
572
|
*,
|
|
570
573
|
serialization_method: str = _DEFAULT_SERIALIZATION_METHOD,
|
|
571
574
|
machine_requirements: MachineRequirements | None = None,
|
|
@@ -598,13 +601,14 @@ class FalServerlessConnection:
|
|
|
598
601
|
else:
|
|
599
602
|
wrapped_requirements = None
|
|
600
603
|
|
|
601
|
-
auth_mode
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
604
|
+
if auth_mode == "public":
|
|
605
|
+
auth = isolate_proto.ApplicationAuthMode.PUBLIC
|
|
606
|
+
elif auth_mode == "shared":
|
|
607
|
+
auth = isolate_proto.ApplicationAuthMode.SHARED
|
|
608
|
+
elif auth_mode == "private":
|
|
609
|
+
auth = isolate_proto.ApplicationAuthMode.PRIVATE
|
|
610
|
+
else:
|
|
611
|
+
auth = None
|
|
608
612
|
|
|
609
613
|
struct_metadata = None
|
|
610
614
|
if metadata:
|
|
@@ -620,7 +624,7 @@ class FalServerlessConnection:
|
|
|
620
624
|
environments=environments,
|
|
621
625
|
machine_requirements=wrapped_requirements,
|
|
622
626
|
application_name=application_name,
|
|
623
|
-
auth_mode=
|
|
627
|
+
auth_mode=auth,
|
|
624
628
|
metadata=struct_metadata,
|
|
625
629
|
deployment_strategy=deployment_strategy_proto,
|
|
626
630
|
scale=scale,
|
|
@@ -730,14 +734,16 @@ class FalServerlessConnection:
|
|
|
730
734
|
self,
|
|
731
735
|
alias: str,
|
|
732
736
|
revision: str,
|
|
733
|
-
auth_mode:
|
|
737
|
+
auth_mode: AuthMode,
|
|
734
738
|
):
|
|
735
739
|
if auth_mode == "public":
|
|
736
740
|
auth = isolate_proto.ApplicationAuthMode.PUBLIC
|
|
737
741
|
elif auth_mode == "shared":
|
|
738
742
|
auth = isolate_proto.ApplicationAuthMode.SHARED
|
|
739
|
-
|
|
743
|
+
elif auth_mode == "private":
|
|
740
744
|
auth = isolate_proto.ApplicationAuthMode.PRIVATE
|
|
745
|
+
else:
|
|
746
|
+
auth = None
|
|
741
747
|
|
|
742
748
|
request = isolate_proto.SetAliasRequest(
|
|
743
749
|
alias=alias,
|
|
@@ -746,10 +752,15 @@ class FalServerlessConnection:
|
|
|
746
752
|
)
|
|
747
753
|
self.stub.SetAlias(request)
|
|
748
754
|
|
|
749
|
-
def delete_alias(self, alias: str) -> str:
|
|
755
|
+
def delete_alias(self, alias: str) -> str | None:
|
|
750
756
|
request = isolate_proto.DeleteAliasRequest(alias=alias)
|
|
751
|
-
|
|
752
|
-
|
|
757
|
+
try:
|
|
758
|
+
res: isolate_proto.DeleteAliasResult = self.stub.DeleteAlias(request)
|
|
759
|
+
return res.revision
|
|
760
|
+
except grpc.RpcError as e:
|
|
761
|
+
if e.code() == grpc.StatusCode.NOT_FOUND:
|
|
762
|
+
return None
|
|
763
|
+
raise
|
|
753
764
|
|
|
754
765
|
def list_aliases(self) -> list[AliasInfo]:
|
|
755
766
|
request = isolate_proto.ListAliasesRequest()
|
|
@@ -6,7 +6,7 @@ import time
|
|
|
6
6
|
import uuid
|
|
7
7
|
from contextlib import contextmanager, suppress
|
|
8
8
|
from datetime import datetime, timedelta, timezone
|
|
9
|
-
from typing import Generator, List, Tuple
|
|
9
|
+
from typing import Generator, List, Tuple, Union
|
|
10
10
|
|
|
11
11
|
import httpx
|
|
12
12
|
import pytest
|
|
@@ -20,7 +20,7 @@ from pydantic import __version__ as pydantic_version
|
|
|
20
20
|
import fal
|
|
21
21
|
import fal.api as api
|
|
22
22
|
from fal import apps
|
|
23
|
-
from fal.app import AppClient, AppClientError
|
|
23
|
+
from fal.app import AppClient, AppClientError, wrap_app
|
|
24
24
|
from fal.cli.deploy import User, _get_user
|
|
25
25
|
from fal.container import ContainerImage
|
|
26
26
|
from fal.exceptions import (
|
|
@@ -235,7 +235,7 @@ class ExceptionApp(fal.App, keep_alive=300, max_concurrency=1):
|
|
|
235
235
|
raise RuntimeError("cuDNN error: CUDNN_STATUS_INTERNAL_ERROR")
|
|
236
236
|
|
|
237
237
|
|
|
238
|
-
class CancellableApp(fal.App, keep_alive=300, max_concurrency=1, request_timeout=
|
|
238
|
+
class CancellableApp(fal.App, keep_alive=300, max_concurrency=1, request_timeout=2):
|
|
239
239
|
task = None
|
|
240
240
|
running = 0
|
|
241
241
|
|
|
@@ -332,7 +332,7 @@ class BrokenApp(fal.App, keep_alive=300, max_concurrency=1):
|
|
|
332
332
|
|
|
333
333
|
|
|
334
334
|
@pytest.fixture(scope="module")
|
|
335
|
-
def host() -> Generator[api.
|
|
335
|
+
def host() -> Generator[api.Host, None, None]:
|
|
336
336
|
yield addition_app.host
|
|
337
337
|
|
|
338
338
|
|
|
@@ -343,7 +343,11 @@ def user() -> Generator[User, None, None]:
|
|
|
343
343
|
|
|
344
344
|
|
|
345
345
|
@contextmanager
|
|
346
|
-
def register_app(
|
|
346
|
+
def register_app(
|
|
347
|
+
host: api.FalServerlessHost,
|
|
348
|
+
app: Union[api.ServedIsolatedFunction, api.IsolatedFunction],
|
|
349
|
+
suffix: str = "",
|
|
350
|
+
):
|
|
347
351
|
app_alias = str(uuid.uuid4()) + "-test-alias" + ("-" + suffix if suffix else "")
|
|
348
352
|
app_revision = host.register(
|
|
349
353
|
func=app.func,
|
|
@@ -398,7 +402,7 @@ def test_fastapi_app(host: api.FalServerlessHost, user: User):
|
|
|
398
402
|
|
|
399
403
|
@pytest.fixture(scope="module")
|
|
400
404
|
def test_stateful_app(host: api.FalServerlessHost, user: User):
|
|
401
|
-
stateful_app =
|
|
405
|
+
stateful_app = wrap_app(StatefulAdditionApp)
|
|
402
406
|
with register_app(host, stateful_app, "stateful") as (app_alias, _):
|
|
403
407
|
yield f"{user.username}/{app_alias}"
|
|
404
408
|
|
|
@@ -411,7 +415,7 @@ def test_pydantic_validation_error():
|
|
|
411
415
|
|
|
412
416
|
@pytest.fixture(scope="module")
|
|
413
417
|
def test_cancellable_app(host: api.FalServerlessHost, user: User):
|
|
414
|
-
cancellable_app =
|
|
418
|
+
cancellable_app = wrap_app(CancellableApp)
|
|
415
419
|
with register_app(host, cancellable_app, "cancellable") as (app_alias, _):
|
|
416
420
|
yield f"{user.username}/{app_alias}"
|
|
417
421
|
|
|
@@ -424,21 +428,21 @@ def test_exception_app():
|
|
|
424
428
|
|
|
425
429
|
@pytest.fixture(scope="module")
|
|
426
430
|
def test_sleep_app(host: api.FalServerlessHost, user: User):
|
|
427
|
-
sleep_app =
|
|
431
|
+
sleep_app = wrap_app(SleepApp)
|
|
428
432
|
with register_app(host, sleep_app, "sleep") as (app_alias, _):
|
|
429
433
|
yield f"{user.username}/{app_alias}"
|
|
430
434
|
|
|
431
435
|
|
|
432
436
|
@pytest.fixture(scope="module")
|
|
433
437
|
def test_realtime_app(host: api.FalServerlessHost, user: User):
|
|
434
|
-
realtime_app =
|
|
438
|
+
realtime_app = wrap_app(RealtimeApp)
|
|
435
439
|
with register_app(host, realtime_app, "realtime") as (app_alias, _):
|
|
436
440
|
yield f"{user.username}/{app_alias}"
|
|
437
441
|
|
|
438
442
|
|
|
439
443
|
def test_broken_app_failure(host: api.FalServerlessHost, user: User):
|
|
440
444
|
with pytest.raises(FalServerlessException) as e:
|
|
441
|
-
|
|
445
|
+
wrap_app(BrokenApp)
|
|
442
446
|
|
|
443
447
|
assert "Failed to generate OpenAPI" in str(e)
|
|
444
448
|
|
|
@@ -551,7 +555,7 @@ def test_app_disconnect_behavior(test_app: str, test_cancellable_app: str):
|
|
|
551
555
|
with pytest.raises(HTTPStatusError) as e:
|
|
552
556
|
apps.run(
|
|
553
557
|
test_cancellable_app,
|
|
554
|
-
arguments={"lhs": 1, "rhs": 2, "wait_time":
|
|
558
|
+
arguments={"lhs": 1, "rhs": 2, "wait_time": 6},
|
|
555
559
|
path="/well-handled",
|
|
556
560
|
)
|
|
557
561
|
assert (
|
|
@@ -571,19 +575,12 @@ def test_app_disconnect_behavior(test_app: str, test_cancellable_app: str):
|
|
|
571
575
|
with pytest.raises(HTTPStatusError) as e:
|
|
572
576
|
apps.run(
|
|
573
577
|
test_cancellable_app,
|
|
574
|
-
arguments={"lhs": 1, "rhs": 2, "wait_time":
|
|
578
|
+
arguments={"lhs": 1, "rhs": 2, "wait_time": 6},
|
|
575
579
|
)
|
|
576
580
|
assert (
|
|
577
581
|
e.value.response.status_code == 504
|
|
578
582
|
), "Expected Gateway Timeout even though the app handled it"
|
|
579
583
|
|
|
580
|
-
with pytest.raises(HTTPStatusError) as e:
|
|
581
|
-
apps.run(
|
|
582
|
-
test_cancellable_app,
|
|
583
|
-
arguments={"lhs": 1, "rhs": 2, "wait_time": 1},
|
|
584
|
-
)
|
|
585
|
-
assert e.value.response.status_code == 500
|
|
586
|
-
|
|
587
584
|
|
|
588
585
|
@pytest.mark.xfail(
|
|
589
586
|
reason="Temporary disabled while investigating backend issue. Ping @efiop"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py
RENAMED
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py
RENAMED
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py
RENAMED
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py
RENAMED
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py
RENAMED
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.25.0 → fal-1.26.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|