fal 0.12.1__tar.gz → 0.12.3__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-0.12.1 → fal-0.12.3}/PKG-INFO +7 -7
- fal-0.12.3/openapi-fal-rest/openapi_fal_rest/models/__init__.py +19 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +26 -0
- fal-0.12.3/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +16 -0
- {fal-0.12.1 → fal-0.12.3}/pyproject.toml +7 -7
- {fal-0.12.1 → fal-0.12.3}/setup.py +8 -26
- {fal-0.12.1 → fal-0.12.3}/src/fal/__init__.py +12 -3
- {fal-0.12.1 → fal-0.12.3}/src/fal/_serialization.py +18 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/api.py +140 -59
- fal-0.12.3/src/fal/app.py +385 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/apps.py +92 -8
- {fal-0.12.1 → fal-0.12.3}/src/fal/auth/__init__.py +20 -1
- {fal-0.12.1 → fal-0.12.3}/src/fal/auth/auth0.py +32 -22
- {fal-0.12.1 → fal-0.12.3}/src/fal/cli.py +34 -52
- fal-0.12.3/src/fal/env.py +3 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/exceptions/handlers.py +3 -2
- {fal-0.12.1 → fal-0.12.3}/src/fal/flags.py +5 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/logging/__init__.py +0 -2
- {fal-0.12.1 → fal-0.12.3}/src/fal/logging/trace.py +8 -1
- {fal-0.12.1 → fal-0.12.3}/src/fal/logging/user.py +2 -1
- {fal-0.12.1 → fal-0.12.3}/src/fal/sdk.py +46 -31
- fal-0.12.3/src/fal/toolkit/__init__.py +30 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/file.py +98 -11
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/providers/fal.py +43 -2
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/types.py +1 -1
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/image/image.py +26 -4
- fal-0.12.3/src/fal/toolkit/optimize.py +50 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/utils/download_utils.py +59 -13
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin/get_invoice_users.py +0 -142
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin/get_usage_per_user.py +0 -199
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin/handle_user_lock.py +0 -191
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin/set_billing_type.py +0 -186
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/applications/get_status_applications_app_user_id_app_alias_or_id_status_get.py +0 -179
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/delete_payment_method.py +0 -162
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_checkout_page.py +0 -198
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_setup_intent_key.py +0 -141
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_invoices.py +0 -152
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_payment_methods.py +0 -152
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_price.py +0 -186
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_spending.py +0 -192
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/handle_stripe_webhook.py +0 -173
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/upcoming_invoice.py +0 -143
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/update_customer_budget.py +0 -183
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/delete.py +0 -162
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/download.py +0 -162
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/file_exists.py +0 -183
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/list_directory.py +0 -173
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/list_root.py +0 -152
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/upload_from_url.py +0 -179
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/health/__init__.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/health/check.py +0 -136
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/keys/__init__.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/keys/create_key.py +0 -188
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/keys/delete_key.py +0 -162
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/keys/list_keys.py +0 -152
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/logs/__init__.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/logs/list_since.py +0 -224
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/requests/__init__.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/requests/requests.py +0 -247
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/storage/__init__.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/storage/get_file_link.py +0 -200
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/storage/initiate_upload.py +0 -172
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/storage/upload_file.py +0 -172
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/tokens/__init__.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/tokens/create_token.py +0 -166
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/__init__.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_custom_usage_per_machine.py +0 -203
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_gateway_request_stats.py +0 -247
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_gateway_request_stats_by_time.py +0 -236
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_gateway_stats_for_yesterday.py +0 -152
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_shared_usage_per_app.py +0 -203
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_usage_records.py +0 -253
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/per_machine_usage.py +0 -218
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/per_machine_usage_details.py +0 -173
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/users/handle_user_registration.py +0 -228
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -87
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/billing_type.py +0 -9
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/body_create_token.py +0 -68
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/body_upload_file.py +0 -75
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/file_spec.py +0 -110
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/gateway_stats_by_time.py +0 -115
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/gateway_usage_stats.py +0 -147
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/get_gateway_request_stats_by_time_response_get_gateway_request_stats_by_time.py +0 -70
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/grouped_usage_detail.py +0 -85
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/handle_stripe_webhook_response_handle_stripe_webhook.py +0 -43
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/initiate_upload_info.py +0 -64
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/invoice.py +0 -129
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/invoice_item.py +0 -85
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/key_scope.py +0 -9
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/log_entry.py +0 -104
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/log_entry_labels.py +0 -43
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/new_user_key.py +0 -64
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/payment_method.py +0 -96
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/per_app_usage_detail.py +0 -88
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/persisted_usage_record.py +0 -118
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/persisted_usage_record_meta.py +0 -43
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/presigned_upload_url.py +0 -64
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/request_io.py +0 -112
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/request_io_json_input.py +0 -43
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/request_io_json_output.py +0 -43
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/run_type.py +0 -9
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/stats_timeframe.py +0 -12
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/status.py +0 -82
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/status_health.py +0 -10
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/uploaded_file_result.py +0 -64
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/url_file_upload.py +0 -57
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/usage_per_machine_type.py +0 -115
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/usage_per_user.py +0 -71
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/usage_run_detail.py +0 -73
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/user_key_info.py +0 -84
- fal-0.12.1/src/fal/app.py +0 -162
- fal-0.12.1/src/fal/env.py +0 -7
- fal-0.12.1/src/fal/logging/datadog.py +0 -77
- fal-0.12.1/src/fal/toolkit/__init__.py +0 -13
- {fal-0.12.1 → fal-0.12.3}/README.md +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin → fal-0.12.3/openapi-fal-rest/openapi_fal_rest/api/applications}/__init__.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/applications → fal-0.12.3/openapi-fal-rest/openapi_fal_rest/api/billing}/__init__.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing → fal-0.12.3/openapi-fal-rest/openapi_fal_rest/api/files}/__init__.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/auth/local.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/console/__init__.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/console/icons.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/console/ux.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/exceptions/__init__.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/exceptions/_base.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/exceptions/auth.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/logging/isolate.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/logging/style.py +0 -0
- fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py → fal-0.12.3/src/fal/py.typed +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/rest_client.py +2 -2
- {fal-0.12.1 → fal-0.12.3}/src/fal/sync.py +3 -3
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/exceptions.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/mainify.py +0 -0
- {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/utils/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.3
|
|
4
4
|
Summary: fal is an easy-to-use Serverless Python Framework
|
|
5
5
|
Author: Features & Labels
|
|
6
6
|
Author-email: hello@fal.ai
|
|
@@ -11,31 +11,31 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Requires-Dist: attrs (>=21.3.0)
|
|
14
|
-
Requires-Dist: auth0-python (>=4.1.0,<5.0.0)
|
|
15
|
-
Requires-Dist: boto3 (>=1.33.8,<2.0.0)
|
|
16
14
|
Requires-Dist: click (>=8.1.3,<9.0.0)
|
|
17
15
|
Requires-Dist: colorama (>=0.4.6,<0.5.0)
|
|
18
|
-
Requires-Dist: datadog-api-client (==2.12.0)
|
|
19
16
|
Requires-Dist: dill (==0.3.7)
|
|
20
17
|
Requires-Dist: fastapi (==0.99.1)
|
|
21
18
|
Requires-Dist: grpc-interceptor (>=0.15.0,<0.16.0)
|
|
22
19
|
Requires-Dist: grpcio (>=1.50.0,<2.0.0)
|
|
23
|
-
Requires-Dist: httpx (>=0.15.4
|
|
20
|
+
Requires-Dist: httpx (>=0.15.4)
|
|
24
21
|
Requires-Dist: importlib-metadata (>=4.4) ; python_version < "3.10"
|
|
25
|
-
Requires-Dist: isolate-proto (
|
|
22
|
+
Requires-Dist: isolate-proto (==0.3.3)
|
|
26
23
|
Requires-Dist: isolate[build] (>=0.12.3,<1.0)
|
|
24
|
+
Requires-Dist: msgpack (>=1.0.7,<2.0.0)
|
|
27
25
|
Requires-Dist: opentelemetry-api (>=1.15.0,<2.0.0)
|
|
28
26
|
Requires-Dist: opentelemetry-sdk (>=1.15.0,<2.0.0)
|
|
29
27
|
Requires-Dist: packaging (>=21.3)
|
|
30
28
|
Requires-Dist: pathspec (>=0.11.1,<0.12.0)
|
|
29
|
+
Requires-Dist: pillow (>=10.2.0,<11.0.0)
|
|
31
30
|
Requires-Dist: portalocker (>=2.7.0,<3.0.0)
|
|
32
31
|
Requires-Dist: pydantic (<2.0)
|
|
32
|
+
Requires-Dist: pyjwt (>=2.8.0,<3.0.0)
|
|
33
33
|
Requires-Dist: python-dateutil (>=2.8.0,<3.0.0)
|
|
34
|
-
Requires-Dist: requests (>=2.28.1,<3.0.0)
|
|
35
34
|
Requires-Dist: rich (>=13.3.2,<14.0.0)
|
|
36
35
|
Requires-Dist: structlog (>=22.3.0,<23.0.0)
|
|
37
36
|
Requires-Dist: types-python-dateutil (>=2.8.0,<3.0.0)
|
|
38
37
|
Requires-Dist: typing-extensions (>=4.7.1,<5.0.0)
|
|
38
|
+
Requires-Dist: websockets (>=12.0,<13.0)
|
|
39
39
|
Description-Content-Type: text/markdown
|
|
40
40
|
|
|
41
41
|
# fal
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
""" Contains all the data models used in inputs/outputs """
|
|
2
|
+
|
|
3
|
+
from .app_metadata_response_app_metadata import AppMetadataResponseAppMetadata
|
|
4
|
+
from .body_upload_local_file import BodyUploadLocalFile
|
|
5
|
+
from .customer_details import CustomerDetails
|
|
6
|
+
from .hash_check import HashCheck
|
|
7
|
+
from .http_validation_error import HTTPValidationError
|
|
8
|
+
from .lock_reason import LockReason
|
|
9
|
+
from .validation_error import ValidationError
|
|
10
|
+
|
|
11
|
+
__all__ = (
|
|
12
|
+
"AppMetadataResponseAppMetadata",
|
|
13
|
+
"BodyUploadLocalFile",
|
|
14
|
+
"CustomerDetails",
|
|
15
|
+
"HashCheck",
|
|
16
|
+
"HTTPValidationError",
|
|
17
|
+
"LockReason",
|
|
18
|
+
"ValidationError",
|
|
19
|
+
)
|
|
@@ -2,6 +2,7 @@ from typing import Any, Dict, List, Type, TypeVar, Union
|
|
|
2
2
|
|
|
3
3
|
import attr
|
|
4
4
|
|
|
5
|
+
from ..models.lock_reason import LockReason
|
|
5
6
|
from ..types import UNSET, Unset
|
|
6
7
|
|
|
7
8
|
T = TypeVar("T", bound="CustomerDetails")
|
|
@@ -16,6 +17,8 @@ class CustomerDetails:
|
|
|
16
17
|
hard_monthly_budget (Union[Unset, int]):
|
|
17
18
|
current_balance (Union[Unset, int]):
|
|
18
19
|
is_paying (Union[Unset, bool]):
|
|
20
|
+
is_locked (Union[Unset, bool]):
|
|
21
|
+
lock_reason (Union[Unset, None, LockReason]): An enumeration.
|
|
19
22
|
"""
|
|
20
23
|
|
|
21
24
|
user_id: str
|
|
@@ -23,6 +26,8 @@ class CustomerDetails:
|
|
|
23
26
|
hard_monthly_budget: Union[Unset, int] = UNSET
|
|
24
27
|
current_balance: Union[Unset, int] = 0
|
|
25
28
|
is_paying: Union[Unset, bool] = False
|
|
29
|
+
is_locked: Union[Unset, bool] = False
|
|
30
|
+
lock_reason: Union[Unset, None, LockReason] = UNSET
|
|
26
31
|
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
|
|
27
32
|
|
|
28
33
|
def to_dict(self) -> Dict[str, Any]:
|
|
@@ -31,6 +36,10 @@ class CustomerDetails:
|
|
|
31
36
|
hard_monthly_budget = self.hard_monthly_budget
|
|
32
37
|
current_balance = self.current_balance
|
|
33
38
|
is_paying = self.is_paying
|
|
39
|
+
is_locked = self.is_locked
|
|
40
|
+
lock_reason: Union[Unset, None, str] = UNSET
|
|
41
|
+
if not isinstance(self.lock_reason, Unset):
|
|
42
|
+
lock_reason = self.lock_reason.value if self.lock_reason else None
|
|
34
43
|
|
|
35
44
|
field_dict: Dict[str, Any] = {}
|
|
36
45
|
field_dict.update(self.additional_properties)
|
|
@@ -47,6 +56,10 @@ class CustomerDetails:
|
|
|
47
56
|
field_dict["current_balance"] = current_balance
|
|
48
57
|
if is_paying is not UNSET:
|
|
49
58
|
field_dict["is_paying"] = is_paying
|
|
59
|
+
if is_locked is not UNSET:
|
|
60
|
+
field_dict["is_locked"] = is_locked
|
|
61
|
+
if lock_reason is not UNSET:
|
|
62
|
+
field_dict["lock_reason"] = lock_reason
|
|
50
63
|
|
|
51
64
|
return field_dict
|
|
52
65
|
|
|
@@ -63,12 +76,25 @@ class CustomerDetails:
|
|
|
63
76
|
|
|
64
77
|
is_paying = d.pop("is_paying", UNSET)
|
|
65
78
|
|
|
79
|
+
is_locked = d.pop("is_locked", UNSET)
|
|
80
|
+
|
|
81
|
+
_lock_reason = d.pop("lock_reason", UNSET)
|
|
82
|
+
lock_reason: Union[Unset, None, LockReason]
|
|
83
|
+
if _lock_reason is None:
|
|
84
|
+
lock_reason = None
|
|
85
|
+
elif isinstance(_lock_reason, Unset):
|
|
86
|
+
lock_reason = UNSET
|
|
87
|
+
else:
|
|
88
|
+
lock_reason = LockReason(_lock_reason)
|
|
89
|
+
|
|
66
90
|
customer_details = cls(
|
|
67
91
|
user_id=user_id,
|
|
68
92
|
soft_monthly_budget=soft_monthly_budget,
|
|
69
93
|
hard_monthly_budget=hard_monthly_budget,
|
|
70
94
|
current_balance=current_balance,
|
|
71
95
|
is_paying=is_paying,
|
|
96
|
+
is_locked=is_locked,
|
|
97
|
+
lock_reason=lock_reason,
|
|
72
98
|
)
|
|
73
99
|
|
|
74
100
|
customer_details.additional_properties = d
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LockReason(str, Enum):
|
|
5
|
+
ADMIN_LOCK_PLEASE_CONTACT_HELLOFAL_AI = "Admin lock. Please contact hello@fal.ai."
|
|
6
|
+
EXHAUSTED_BALANCE_TOP_UP_YOUR_BALANCE_AT_FAL_AIDASHBOARDBILLING = (
|
|
7
|
+
"Exhausted balance. Top up your balance at fal.ai/dashboard/billing"
|
|
8
|
+
)
|
|
9
|
+
PLEASE_ADD_A_PAYMENT_METHOD_AT_FAL_AIDASHBOARDBILLING = "Please add a payment method at fal.ai/dashboard/billing"
|
|
10
|
+
UNKNOWN_PLEASE_CONTACT_HELLOFAL_AI = "Unknown. Please contact hello@fal.ai."
|
|
11
|
+
USER_BUDGET_IS_EXCEEDED_ADJUST_IT_AT_FAL_AIDASHBOARDBILLING = (
|
|
12
|
+
"User budget is exceeded. Adjust it at fal.ai/dashboard/billing"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def __str__(self) -> str:
|
|
16
|
+
return str(self.value)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "fal"
|
|
3
|
-
version = "0.12.
|
|
3
|
+
version = "0.12.3"
|
|
4
4
|
description = "fal is an easy-to-use Serverless Python Framework"
|
|
5
5
|
authors = ["Features & Labels <hello@fal.ai>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -12,15 +12,12 @@ packages = [
|
|
|
12
12
|
[tool.poetry.dependencies]
|
|
13
13
|
python = "^3.8"
|
|
14
14
|
isolate = {version = ">=0.12.3, <1.0", extras = ["build"]}
|
|
15
|
-
isolate-proto = "
|
|
16
|
-
auth0-python = "^4.1.0"
|
|
17
|
-
requests = "^2.28.1"
|
|
15
|
+
isolate-proto = "0.3.3"
|
|
18
16
|
grpcio = "^1.50.0"
|
|
19
17
|
dill = "0.3.7"
|
|
20
18
|
typing-extensions = "^4.7.1"
|
|
21
19
|
click = "^8.1.3"
|
|
22
20
|
structlog = "^22.3.0"
|
|
23
|
-
datadog-api-client = "2.12.0"
|
|
24
21
|
opentelemetry-api = "^1.15.0"
|
|
25
22
|
opentelemetry-sdk = "^1.15.0"
|
|
26
23
|
grpc-interceptor = "^0.15.0"
|
|
@@ -35,14 +32,17 @@ pydantic = "<2.0"
|
|
|
35
32
|
fastapi = "0.99.1"
|
|
36
33
|
|
|
37
34
|
# rest-api-client dependencies
|
|
38
|
-
httpx = ">=0.15.4
|
|
35
|
+
httpx = ">=0.15.4"
|
|
39
36
|
attrs = ">=21.3.0"
|
|
40
37
|
python-dateutil = "^2.8.0"
|
|
41
38
|
types-python-dateutil = "^2.8.0"
|
|
42
39
|
|
|
43
40
|
# For 3.9 and earlier, importlib-metadata's newer versions are included in the standard library.
|
|
44
41
|
importlib-metadata = { version = ">=4.4", python = "<3.10" }
|
|
45
|
-
|
|
42
|
+
msgpack = "^1.0.7"
|
|
43
|
+
websockets = "^12.0"
|
|
44
|
+
pillow = "^10.2.0"
|
|
45
|
+
pyjwt = "^2.8.0"
|
|
46
46
|
|
|
47
47
|
[tool.poetry.group.dev.dependencies]
|
|
48
48
|
openapi-python-client = "^0.14.1"
|
|
@@ -5,18 +5,9 @@ package_dir = \
|
|
|
5
5
|
{'': 'src',
|
|
6
6
|
'openapi_fal_rest': 'openapi-fal-rest/openapi_fal_rest',
|
|
7
7
|
'openapi_fal_rest.api': 'openapi-fal-rest/openapi_fal_rest/api',
|
|
8
|
-
'openapi_fal_rest.api.admin': 'openapi-fal-rest/openapi_fal_rest/api/admin',
|
|
9
8
|
'openapi_fal_rest.api.applications': 'openapi-fal-rest/openapi_fal_rest/api/applications',
|
|
10
9
|
'openapi_fal_rest.api.billing': 'openapi-fal-rest/openapi_fal_rest/api/billing',
|
|
11
10
|
'openapi_fal_rest.api.files': 'openapi-fal-rest/openapi_fal_rest/api/files',
|
|
12
|
-
'openapi_fal_rest.api.health': 'openapi-fal-rest/openapi_fal_rest/api/health',
|
|
13
|
-
'openapi_fal_rest.api.keys': 'openapi-fal-rest/openapi_fal_rest/api/keys',
|
|
14
|
-
'openapi_fal_rest.api.logs': 'openapi-fal-rest/openapi_fal_rest/api/logs',
|
|
15
|
-
'openapi_fal_rest.api.requests': 'openapi-fal-rest/openapi_fal_rest/api/requests',
|
|
16
|
-
'openapi_fal_rest.api.storage': 'openapi-fal-rest/openapi_fal_rest/api/storage',
|
|
17
|
-
'openapi_fal_rest.api.tokens': 'openapi-fal-rest/openapi_fal_rest/api/tokens',
|
|
18
|
-
'openapi_fal_rest.api.usage': 'openapi-fal-rest/openapi_fal_rest/api/usage',
|
|
19
|
-
'openapi_fal_rest.api.users': 'openapi-fal-rest/openapi_fal_rest/api/users',
|
|
20
11
|
'openapi_fal_rest.models': 'openapi-fal-rest/openapi_fal_rest/models'}
|
|
21
12
|
|
|
22
13
|
packages = \
|
|
@@ -32,18 +23,9 @@ packages = \
|
|
|
32
23
|
'fal.toolkit.utils',
|
|
33
24
|
'openapi_fal_rest',
|
|
34
25
|
'openapi_fal_rest.api',
|
|
35
|
-
'openapi_fal_rest.api.admin',
|
|
36
26
|
'openapi_fal_rest.api.applications',
|
|
37
27
|
'openapi_fal_rest.api.billing',
|
|
38
28
|
'openapi_fal_rest.api.files',
|
|
39
|
-
'openapi_fal_rest.api.health',
|
|
40
|
-
'openapi_fal_rest.api.keys',
|
|
41
|
-
'openapi_fal_rest.api.logs',
|
|
42
|
-
'openapi_fal_rest.api.requests',
|
|
43
|
-
'openapi_fal_rest.api.storage',
|
|
44
|
-
'openapi_fal_rest.api.tokens',
|
|
45
|
-
'openapi_fal_rest.api.usage',
|
|
46
|
-
'openapi_fal_rest.api.users',
|
|
47
29
|
'openapi_fal_rest.models']
|
|
48
30
|
|
|
49
31
|
package_data = \
|
|
@@ -51,30 +33,30 @@ package_data = \
|
|
|
51
33
|
|
|
52
34
|
install_requires = \
|
|
53
35
|
['attrs>=21.3.0',
|
|
54
|
-
'auth0-python>=4.1.0,<5.0.0',
|
|
55
|
-
'boto3>=1.33.8,<2.0.0',
|
|
56
36
|
'click>=8.1.3,<9.0.0',
|
|
57
37
|
'colorama>=0.4.6,<0.5.0',
|
|
58
|
-
'datadog-api-client==2.12.0',
|
|
59
38
|
'dill==0.3.7',
|
|
60
39
|
'fastapi==0.99.1',
|
|
61
40
|
'grpc-interceptor>=0.15.0,<0.16.0',
|
|
62
41
|
'grpcio>=1.50.0,<2.0.0',
|
|
63
|
-
'httpx>=0.15.4
|
|
64
|
-
'isolate-proto
|
|
42
|
+
'httpx>=0.15.4',
|
|
43
|
+
'isolate-proto==0.3.3',
|
|
65
44
|
'isolate[build]>=0.12.3,<1.0',
|
|
45
|
+
'msgpack>=1.0.7,<2.0.0',
|
|
66
46
|
'opentelemetry-api>=1.15.0,<2.0.0',
|
|
67
47
|
'opentelemetry-sdk>=1.15.0,<2.0.0',
|
|
68
48
|
'packaging>=21.3',
|
|
69
49
|
'pathspec>=0.11.1,<0.12.0',
|
|
50
|
+
'pillow>=10.2.0,<11.0.0',
|
|
70
51
|
'portalocker>=2.7.0,<3.0.0',
|
|
71
52
|
'pydantic<2.0',
|
|
53
|
+
'pyjwt>=2.8.0,<3.0.0',
|
|
72
54
|
'python-dateutil>=2.8.0,<3.0.0',
|
|
73
|
-
'requests>=2.28.1,<3.0.0',
|
|
74
55
|
'rich>=13.3.2,<14.0.0',
|
|
75
56
|
'structlog>=22.3.0,<23.0.0',
|
|
76
57
|
'types-python-dateutil>=2.8.0,<3.0.0',
|
|
77
|
-
'typing-extensions>=4.7.1,<5.0.0'
|
|
58
|
+
'typing-extensions>=4.7.1,<5.0.0',
|
|
59
|
+
'websockets>=12.0,<13.0']
|
|
78
60
|
|
|
79
61
|
extras_require = \
|
|
80
62
|
{':python_version < "3.10"': ['importlib-metadata>=4.4']}
|
|
@@ -84,7 +66,7 @@ entry_points = \
|
|
|
84
66
|
|
|
85
67
|
setup_kwargs = {
|
|
86
68
|
'name': 'fal',
|
|
87
|
-
'version': '0.12.
|
|
69
|
+
'version': '0.12.3',
|
|
88
70
|
'description': 'fal is an easy-to-use Serverless Python Framework',
|
|
89
71
|
'long_description': '# fal\n\nfal is a serverless Python runtime that lets you run and scale code in the cloud with no infra management.\n\nWith fal, you can build pipelines, serve ML models and scale them up to many users. You scale down to 0 when you don\'t use any resources.\n\n## Quickstart\n\nFirst, you need to install the `fal` package. You can do so using pip:\n```shell\npip install fal\n```\n\nThen you need to authenticate:\n```shell\nfal auth login\n```\n\nYou can also use fal keys that you can get from [our dashboard](https://fal.ai/dashboard/keys).\n\nNow can use the fal package in your Python scripts as follows:\n\n```py\nimport fal\n\n@fal.function(\n "virtualenv",\n requirements=["pyjokes"],\n)\ndef tell_joke() -> str:\n import pyjokes\n\n joke = pyjokes.get_joke()\n return joke\n\nprint("Joke from the clouds: ", tell_joke())\n```\n\nA new virtual environment will be created by fal in the cloud and the set of requirements that we passed will be installed as soon as this function is called. From that point on, our code will be executed as if it were running locally, and the joke prepared by the pyjokes library will be returned.\n\n## Next steps\n\nIf you would like to find out more about the capabilities of fal, check out to the [docs](https://fal.ai/docs). You can learn more about persistent storage, function caches and deploying your functions as API endpoints.\n',
|
|
90
72
|
'author': 'Features & Labels',
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from fal import apps
|
|
4
|
-
|
|
5
|
-
# TODO: DEPRECATED - use function instead
|
|
6
4
|
from fal.api import FalServerlessHost, LocalHost, cached
|
|
7
5
|
from fal.api import function
|
|
8
6
|
from fal.api import function as isolated
|
|
9
|
-
from fal.app import App, endpoint, wrap_app
|
|
7
|
+
from fal.app import App, endpoint, realtime, wrap_app
|
|
10
8
|
from fal.sdk import FalServerlessKeyCredentials
|
|
11
9
|
from fal.sync import sync_dir
|
|
12
10
|
|
|
@@ -16,6 +14,17 @@ serverless = FalServerlessHost()
|
|
|
16
14
|
# DEPRECATED - use serverless instead
|
|
17
15
|
cloud = FalServerlessHost()
|
|
18
16
|
|
|
17
|
+
__all__ = [
|
|
18
|
+
"function",
|
|
19
|
+
"cached",
|
|
20
|
+
"App",
|
|
21
|
+
"endpoint",
|
|
22
|
+
"realtime",
|
|
23
|
+
# "wrap_app",
|
|
24
|
+
"FalServerlessKeyCredentials",
|
|
25
|
+
"sync_dir",
|
|
26
|
+
]
|
|
27
|
+
|
|
19
28
|
|
|
20
29
|
# NOTE: This makes `import fal.dbt` import the `dbt-fal` module and `import fal` import the `fal` module
|
|
21
30
|
# NOTE: taken from dbt-core: https://github.com/dbt-labs/dbt-core/blob/ac539fd5cf325cfb5315339077d03399d575f570/core/dbt/adapters/__init__.py#L1-L7
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from functools import wraps
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import dill
|
|
6
7
|
from dill import _dill
|
|
8
|
+
|
|
7
9
|
from fal.toolkit import mainify
|
|
8
10
|
|
|
9
11
|
# each @fal.function gets added to this set so that we can
|
|
@@ -49,12 +51,28 @@ def by_value_locator(obj, pickler=None, og_locator=_dill._locate_function):
|
|
|
49
51
|
_dill._locate_function = by_value_locator
|
|
50
52
|
|
|
51
53
|
|
|
54
|
+
def include_packages_from_path(raw_path: str):
|
|
55
|
+
path = Path(raw_path).resolve()
|
|
56
|
+
parent = path
|
|
57
|
+
while (parent.parent / "__init__.py").exists():
|
|
58
|
+
parent = parent.parent
|
|
59
|
+
|
|
60
|
+
if parent != path:
|
|
61
|
+
_PACKAGES.add(parent.name)
|
|
62
|
+
|
|
63
|
+
|
|
52
64
|
def add_serialization_listeners_for(obj):
|
|
53
65
|
module_name = getattr(obj, "__module__", None)
|
|
54
66
|
if not module_name:
|
|
55
67
|
return None
|
|
56
68
|
|
|
57
69
|
_MODULES.add(module_name)
|
|
70
|
+
if module_name == "__main__":
|
|
71
|
+
# When the module is __main__, we need to recursively go up the
|
|
72
|
+
# tree to locate the actual package name.
|
|
73
|
+
import __main__
|
|
74
|
+
|
|
75
|
+
include_packages_from_path(__main__.__file__)
|
|
58
76
|
|
|
59
77
|
if "." in module_name:
|
|
60
78
|
package_name, *_ = module_name.partition(".")
|
|
@@ -4,7 +4,7 @@ import inspect
|
|
|
4
4
|
import sys
|
|
5
5
|
from collections import defaultdict
|
|
6
6
|
from concurrent.futures import ThreadPoolExecutor
|
|
7
|
-
from contextlib import suppress
|
|
7
|
+
from contextlib import asynccontextmanager, suppress
|
|
8
8
|
from dataclasses import dataclass, field, replace
|
|
9
9
|
from functools import partial, wraps
|
|
10
10
|
from os import PathLike
|
|
@@ -16,6 +16,7 @@ from typing import (
|
|
|
16
16
|
Generic,
|
|
17
17
|
Iterator,
|
|
18
18
|
Literal,
|
|
19
|
+
NamedTuple,
|
|
19
20
|
TypeVar,
|
|
20
21
|
cast,
|
|
21
22
|
overload,
|
|
@@ -23,10 +24,18 @@ from typing import (
|
|
|
23
24
|
|
|
24
25
|
import dill
|
|
25
26
|
import dill.detect
|
|
26
|
-
import fal.flags as flags
|
|
27
27
|
import grpc
|
|
28
28
|
import isolate
|
|
29
29
|
import yaml
|
|
30
|
+
from fastapi import FastAPI
|
|
31
|
+
from isolate.backends.common import active_python
|
|
32
|
+
from isolate.backends.settings import DEFAULT_SETTINGS
|
|
33
|
+
from isolate.connections import PythonIPC
|
|
34
|
+
from packaging.requirements import Requirement
|
|
35
|
+
from packaging.utils import canonicalize_name
|
|
36
|
+
from typing_extensions import Concatenate, ParamSpec
|
|
37
|
+
|
|
38
|
+
import fal.flags as flags
|
|
30
39
|
from fal._serialization import add_serialization_listeners_for, patch_dill
|
|
31
40
|
from fal.logging.isolate import IsolateLogPrinter
|
|
32
41
|
from fal.sdk import (
|
|
@@ -38,16 +47,10 @@ from fal.sdk import (
|
|
|
38
47
|
FalServerlessConnection,
|
|
39
48
|
HostedRunState,
|
|
40
49
|
MachineRequirements,
|
|
41
|
-
|
|
50
|
+
get_agent_credentials,
|
|
42
51
|
get_default_credentials,
|
|
43
52
|
)
|
|
44
53
|
from fal.toolkit import mainify
|
|
45
|
-
from isolate.backends.common import active_python
|
|
46
|
-
from isolate.backends.settings import DEFAULT_SETTINGS
|
|
47
|
-
from isolate.connections import PythonIPC
|
|
48
|
-
from packaging.requirements import Requirement
|
|
49
|
-
from packaging.utils import canonicalize_name
|
|
50
|
-
from typing_extensions import Concatenate, ParamSpec
|
|
51
54
|
|
|
52
55
|
ArgsT = ParamSpec("ArgsT")
|
|
53
56
|
ReturnT = TypeVar("ReturnT", covariant=True)
|
|
@@ -55,6 +58,8 @@ ReturnT = TypeVar("ReturnT", covariant=True)
|
|
|
55
58
|
BasicConfig = Dict[str, Any]
|
|
56
59
|
_UNSET = object()
|
|
57
60
|
|
|
61
|
+
SERVE_REQUIREMENTS = ["fastapi==0.99.1", "uvicorn"]
|
|
62
|
+
|
|
58
63
|
|
|
59
64
|
@dataclass
|
|
60
65
|
class FalServerlessError(Exception):
|
|
@@ -109,7 +114,7 @@ class Host(Generic[ArgsT, ReturnT]):
|
|
|
109
114
|
options.environment[key] = value
|
|
110
115
|
|
|
111
116
|
if options.gateway.get("serve"):
|
|
112
|
-
options.add_requirements(
|
|
117
|
+
options.add_requirements(SERVE_REQUIREMENTS)
|
|
113
118
|
|
|
114
119
|
return options
|
|
115
120
|
|
|
@@ -328,7 +333,7 @@ class FalServerlessHost(Host):
|
|
|
328
333
|
|
|
329
334
|
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
330
335
|
self.__dict__.update(state)
|
|
331
|
-
self.credentials =
|
|
336
|
+
self.credentials = get_agent_credentials(self.credentials)
|
|
332
337
|
|
|
333
338
|
@property
|
|
334
339
|
def _connection(self) -> FalServerlessConnection:
|
|
@@ -729,53 +734,17 @@ def function( # type: ignore
|
|
|
729
734
|
|
|
730
735
|
|
|
731
736
|
@mainify
|
|
732
|
-
class
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
self._func = func
|
|
737
|
-
|
|
738
|
-
def build_app(self):
|
|
739
|
-
from fastapi import FastAPI
|
|
740
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
741
|
-
|
|
742
|
-
_app = FastAPI()
|
|
743
|
-
|
|
744
|
-
_app.add_middleware(
|
|
745
|
-
CORSMiddleware,
|
|
746
|
-
allow_credentials=True,
|
|
747
|
-
allow_headers=("*"),
|
|
748
|
-
allow_methods=("*"),
|
|
749
|
-
allow_origins=("*"),
|
|
750
|
-
)
|
|
751
|
-
|
|
752
|
-
_app.add_api_route(
|
|
753
|
-
"/",
|
|
754
|
-
self._func, # type: ignore
|
|
755
|
-
name=self._func.__name__,
|
|
756
|
-
methods=["POST"],
|
|
757
|
-
)
|
|
758
|
-
|
|
759
|
-
return _app
|
|
760
|
-
|
|
761
|
-
def __call__(self, *args, **kwargs) -> None:
|
|
762
|
-
if len(args) != 0 or len(kwargs) != 0:
|
|
763
|
-
print(
|
|
764
|
-
f"[warning] {self._func.__name__} function is served with no arguments."
|
|
765
|
-
)
|
|
766
|
-
|
|
767
|
-
from uvicorn import run
|
|
768
|
-
|
|
769
|
-
app = self.build_app()
|
|
770
|
-
run(app, host="0.0.0.0", port=8080)
|
|
737
|
+
class FalFastAPI(FastAPI):
|
|
738
|
+
"""
|
|
739
|
+
A subclass of FastAPI that adds some fal-specific functionality.
|
|
740
|
+
"""
|
|
771
741
|
|
|
772
742
|
def openapi(self) -> dict[str, Any]:
|
|
773
743
|
"""
|
|
774
744
|
Build the OpenAPI specification for the served function.
|
|
775
745
|
Attach needed metadata for a better integration to fal.
|
|
776
746
|
"""
|
|
777
|
-
|
|
778
|
-
spec = app.openapi()
|
|
747
|
+
spec = super().openapi()
|
|
779
748
|
self._mark_order_openapi(spec)
|
|
780
749
|
return spec
|
|
781
750
|
|
|
@@ -787,7 +756,8 @@ class ServeWrapper:
|
|
|
787
756
|
"""
|
|
788
757
|
|
|
789
758
|
def mark_order(obj: dict[str, Any], key: str):
|
|
790
|
-
|
|
759
|
+
if key in obj:
|
|
760
|
+
obj[f"x-fal-order-{key}"] = list(obj[key].keys())
|
|
791
761
|
|
|
792
762
|
mark_order(spec, "paths")
|
|
793
763
|
|
|
@@ -796,18 +766,129 @@ class ServeWrapper:
|
|
|
796
766
|
Mark the order of properties in the schema object.
|
|
797
767
|
They can have 'allOf', 'properties' or '$ref' key.
|
|
798
768
|
"""
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
mark_order(schema, "properties")
|
|
769
|
+
for sub_schema in schema.get("allOf", []):
|
|
770
|
+
order_schema_object(sub_schema)
|
|
771
|
+
|
|
772
|
+
mark_order(schema, "properties")
|
|
804
773
|
|
|
805
|
-
for key in spec.get("components", {}).get("schemas"
|
|
774
|
+
for key in spec.get("components", {}).get("schemas", {}):
|
|
806
775
|
order_schema_object(spec["components"]["schemas"][key])
|
|
807
776
|
|
|
808
777
|
return spec
|
|
809
778
|
|
|
810
779
|
|
|
780
|
+
@mainify
|
|
781
|
+
class RouteSignature(NamedTuple):
|
|
782
|
+
path: str
|
|
783
|
+
is_websocket: bool = False
|
|
784
|
+
input_modal: type | None = None
|
|
785
|
+
buffering: int | None = None
|
|
786
|
+
session_timeout: float | None = None
|
|
787
|
+
max_batch_size: int = 1
|
|
788
|
+
emit_timings: bool = False
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
@mainify
|
|
792
|
+
class BaseServable:
|
|
793
|
+
def collect_routes(self) -> dict[RouteSignature, Callable[..., Any]]:
|
|
794
|
+
raise NotImplementedError
|
|
795
|
+
|
|
796
|
+
def _add_extra_middlewares(self, app: FastAPI):
|
|
797
|
+
"""
|
|
798
|
+
For subclasses to add extra middlewares to the app.
|
|
799
|
+
"""
|
|
800
|
+
pass
|
|
801
|
+
|
|
802
|
+
@asynccontextmanager
|
|
803
|
+
async def lifespan(self, app: FastAPI):
|
|
804
|
+
yield
|
|
805
|
+
|
|
806
|
+
def _build_app(self) -> FastAPI:
|
|
807
|
+
from fastapi import HTTPException, Request
|
|
808
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
809
|
+
from fastapi.responses import JSONResponse
|
|
810
|
+
|
|
811
|
+
_app = FalFastAPI(lifespan=self.lifespan)
|
|
812
|
+
|
|
813
|
+
_app.add_middleware(
|
|
814
|
+
CORSMiddleware,
|
|
815
|
+
allow_credentials=True,
|
|
816
|
+
allow_headers=("*"),
|
|
817
|
+
allow_methods=("*"),
|
|
818
|
+
allow_origins=("*"),
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
self._add_extra_middlewares(_app)
|
|
822
|
+
|
|
823
|
+
@_app.exception_handler(404)
|
|
824
|
+
async def not_found_exception_handler(request: Request, exc: HTTPException):
|
|
825
|
+
# Rewrite the message to include the path that was not found.
|
|
826
|
+
# This is supposed to make it easier to understand to the user
|
|
827
|
+
# that the error comes from the app and not our platform.
|
|
828
|
+
if exc.detail == "Not Found":
|
|
829
|
+
return JSONResponse(
|
|
830
|
+
{"detail": f"Path {request.url.path} not found"}, 404
|
|
831
|
+
)
|
|
832
|
+
else:
|
|
833
|
+
# If it's not a generic 404, just return the original message.
|
|
834
|
+
return JSONResponse({"detail": exc.detail}, 404)
|
|
835
|
+
|
|
836
|
+
routes = self.collect_routes()
|
|
837
|
+
if not routes:
|
|
838
|
+
raise ValueError("An application must have at least one route!")
|
|
839
|
+
|
|
840
|
+
for signature, endpoint in routes.items():
|
|
841
|
+
if signature.is_websocket:
|
|
842
|
+
_app.add_api_websocket_route(
|
|
843
|
+
signature.path,
|
|
844
|
+
endpoint,
|
|
845
|
+
name=endpoint.__name__,
|
|
846
|
+
)
|
|
847
|
+
else:
|
|
848
|
+
_app.add_api_route(
|
|
849
|
+
signature.path,
|
|
850
|
+
endpoint,
|
|
851
|
+
name=endpoint.__name__,
|
|
852
|
+
methods=["POST"],
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
return _app
|
|
856
|
+
|
|
857
|
+
def openapi(self) -> dict[str, Any]:
|
|
858
|
+
"""
|
|
859
|
+
Build the OpenAPI specification for the served function.
|
|
860
|
+
Attach needed metadata for a better integration to fal.
|
|
861
|
+
"""
|
|
862
|
+
return self._build_app().openapi()
|
|
863
|
+
|
|
864
|
+
def serve(self) -> None:
|
|
865
|
+
import uvicorn
|
|
866
|
+
|
|
867
|
+
app = self._build_app()
|
|
868
|
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
@mainify
|
|
872
|
+
class ServeWrapper(BaseServable):
|
|
873
|
+
_func: Callable
|
|
874
|
+
|
|
875
|
+
def __init__(self, func: Callable):
|
|
876
|
+
self._func = func
|
|
877
|
+
|
|
878
|
+
def collect_routes(self) -> dict[RouteSignature, Callable[..., Any]]:
|
|
879
|
+
return {
|
|
880
|
+
RouteSignature("/"): self._func,
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
def __call__(self, *args, **kwargs) -> None:
|
|
884
|
+
if len(args) != 0 or len(kwargs) != 0:
|
|
885
|
+
print(
|
|
886
|
+
f"[warning] {self._func.__name__} function is served with no arguments."
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
self.serve()
|
|
890
|
+
|
|
891
|
+
|
|
811
892
|
@dataclass
|
|
812
893
|
class IsolatedFunction(Generic[ArgsT, ReturnT]):
|
|
813
894
|
host: Host[ArgsT, ReturnT]
|