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.

Files changed (153) hide show
  1. {fal-0.12.1 → fal-0.12.3}/PKG-INFO +7 -7
  2. fal-0.12.3/openapi-fal-rest/openapi_fal_rest/models/__init__.py +19 -0
  3. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +26 -0
  4. fal-0.12.3/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +16 -0
  5. {fal-0.12.1 → fal-0.12.3}/pyproject.toml +7 -7
  6. {fal-0.12.1 → fal-0.12.3}/setup.py +8 -26
  7. {fal-0.12.1 → fal-0.12.3}/src/fal/__init__.py +12 -3
  8. {fal-0.12.1 → fal-0.12.3}/src/fal/_serialization.py +18 -0
  9. {fal-0.12.1 → fal-0.12.3}/src/fal/api.py +140 -59
  10. fal-0.12.3/src/fal/app.py +385 -0
  11. {fal-0.12.1 → fal-0.12.3}/src/fal/apps.py +92 -8
  12. {fal-0.12.1 → fal-0.12.3}/src/fal/auth/__init__.py +20 -1
  13. {fal-0.12.1 → fal-0.12.3}/src/fal/auth/auth0.py +32 -22
  14. {fal-0.12.1 → fal-0.12.3}/src/fal/cli.py +34 -52
  15. fal-0.12.3/src/fal/env.py +3 -0
  16. {fal-0.12.1 → fal-0.12.3}/src/fal/exceptions/handlers.py +3 -2
  17. {fal-0.12.1 → fal-0.12.3}/src/fal/flags.py +5 -0
  18. {fal-0.12.1 → fal-0.12.3}/src/fal/logging/__init__.py +0 -2
  19. {fal-0.12.1 → fal-0.12.3}/src/fal/logging/trace.py +8 -1
  20. {fal-0.12.1 → fal-0.12.3}/src/fal/logging/user.py +2 -1
  21. {fal-0.12.1 → fal-0.12.3}/src/fal/sdk.py +46 -31
  22. fal-0.12.3/src/fal/toolkit/__init__.py +30 -0
  23. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/file.py +98 -11
  24. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/providers/fal.py +43 -2
  25. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/types.py +1 -1
  26. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/image/image.py +26 -4
  27. fal-0.12.3/src/fal/toolkit/optimize.py +50 -0
  28. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/utils/download_utils.py +59 -13
  29. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin/get_invoice_users.py +0 -142
  30. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin/get_usage_per_user.py +0 -199
  31. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin/handle_user_lock.py +0 -191
  32. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/admin/set_billing_type.py +0 -186
  33. 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
  34. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/delete_payment_method.py +0 -162
  35. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_checkout_page.py +0 -198
  36. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_setup_intent_key.py +0 -141
  37. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_invoices.py +0 -152
  38. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_payment_methods.py +0 -152
  39. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_price.py +0 -186
  40. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_spending.py +0 -192
  41. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/handle_stripe_webhook.py +0 -173
  42. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/upcoming_invoice.py +0 -143
  43. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/billing/update_customer_budget.py +0 -183
  44. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/delete.py +0 -162
  45. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/download.py +0 -162
  46. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/file_exists.py +0 -183
  47. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/list_directory.py +0 -173
  48. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/list_root.py +0 -152
  49. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/upload_from_url.py +0 -179
  50. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/health/__init__.py +0 -0
  51. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/health/check.py +0 -136
  52. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/keys/__init__.py +0 -0
  53. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/keys/create_key.py +0 -188
  54. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/keys/delete_key.py +0 -162
  55. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/keys/list_keys.py +0 -152
  56. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/logs/__init__.py +0 -0
  57. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/logs/list_since.py +0 -224
  58. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/requests/__init__.py +0 -0
  59. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/requests/requests.py +0 -247
  60. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/storage/__init__.py +0 -0
  61. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/storage/get_file_link.py +0 -200
  62. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/storage/initiate_upload.py +0 -172
  63. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/storage/upload_file.py +0 -172
  64. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/tokens/__init__.py +0 -0
  65. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/tokens/create_token.py +0 -166
  66. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/__init__.py +0 -0
  67. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_custom_usage_per_machine.py +0 -203
  68. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_gateway_request_stats.py +0 -247
  69. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_gateway_request_stats_by_time.py +0 -236
  70. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_gateway_stats_for_yesterday.py +0 -152
  71. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_shared_usage_per_app.py +0 -203
  72. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/get_usage_records.py +0 -253
  73. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/per_machine_usage.py +0 -218
  74. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/usage/per_machine_usage_details.py +0 -173
  75. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  76. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/users/handle_user_registration.py +0 -228
  77. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -87
  78. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/billing_type.py +0 -9
  79. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/body_create_token.py +0 -68
  80. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/body_upload_file.py +0 -75
  81. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/file_spec.py +0 -110
  82. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/gateway_stats_by_time.py +0 -115
  83. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/gateway_usage_stats.py +0 -147
  84. 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
  85. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/grouped_usage_detail.py +0 -85
  86. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/handle_stripe_webhook_response_handle_stripe_webhook.py +0 -43
  87. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/initiate_upload_info.py +0 -64
  88. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/invoice.py +0 -129
  89. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/invoice_item.py +0 -85
  90. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/key_scope.py +0 -9
  91. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/log_entry.py +0 -104
  92. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/log_entry_labels.py +0 -43
  93. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/new_user_key.py +0 -64
  94. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/payment_method.py +0 -96
  95. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/per_app_usage_detail.py +0 -88
  96. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/persisted_usage_record.py +0 -118
  97. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/persisted_usage_record_meta.py +0 -43
  98. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/presigned_upload_url.py +0 -64
  99. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/request_io.py +0 -112
  100. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/request_io_json_input.py +0 -43
  101. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/request_io_json_output.py +0 -43
  102. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/run_type.py +0 -9
  103. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/stats_timeframe.py +0 -12
  104. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/status.py +0 -82
  105. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/status_health.py +0 -10
  106. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/uploaded_file_result.py +0 -64
  107. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/url_file_upload.py +0 -57
  108. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/usage_per_machine_type.py +0 -115
  109. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/usage_per_user.py +0 -71
  110. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/usage_run_detail.py +0 -73
  111. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/models/user_key_info.py +0 -84
  112. fal-0.12.1/src/fal/app.py +0 -162
  113. fal-0.12.1/src/fal/env.py +0 -7
  114. fal-0.12.1/src/fal/logging/datadog.py +0 -77
  115. fal-0.12.1/src/fal/toolkit/__init__.py +0 -13
  116. {fal-0.12.1 → fal-0.12.3}/README.md +0 -0
  117. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  118. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  119. {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
  120. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  121. {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
  122. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  123. {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
  124. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  125. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  126. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  127. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  128. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  129. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  130. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  131. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  132. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  133. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  134. {fal-0.12.1 → fal-0.12.3}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  135. {fal-0.12.1 → fal-0.12.3}/src/fal/auth/local.py +0 -0
  136. {fal-0.12.1 → fal-0.12.3}/src/fal/console/__init__.py +0 -0
  137. {fal-0.12.1 → fal-0.12.3}/src/fal/console/icons.py +0 -0
  138. {fal-0.12.1 → fal-0.12.3}/src/fal/console/ux.py +0 -0
  139. {fal-0.12.1 → fal-0.12.3}/src/fal/exceptions/__init__.py +0 -0
  140. {fal-0.12.1 → fal-0.12.3}/src/fal/exceptions/_base.py +0 -0
  141. {fal-0.12.1 → fal-0.12.3}/src/fal/exceptions/auth.py +0 -0
  142. {fal-0.12.1 → fal-0.12.3}/src/fal/logging/isolate.py +0 -0
  143. {fal-0.12.1 → fal-0.12.3}/src/fal/logging/style.py +0 -0
  144. fal-0.12.1/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py → fal-0.12.3/src/fal/py.typed +0 -0
  145. {fal-0.12.1 → fal-0.12.3}/src/fal/rest_client.py +2 -2
  146. {fal-0.12.1 → fal-0.12.3}/src/fal/sync.py +3 -3
  147. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/exceptions.py +0 -0
  148. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/__init__.py +0 -0
  149. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/providers/gcp.py +0 -0
  150. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/file/providers/r2.py +0 -0
  151. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/image/__init__.py +0 -0
  152. {fal-0.12.1 → fal-0.12.3}/src/fal/toolkit/mainify.py +0 -0
  153. {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.1
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,<0.25.0)
20
+ Requires-Dist: httpx (>=0.15.4)
24
21
  Requires-Dist: importlib-metadata (>=4.4) ; python_version < "3.10"
25
- Requires-Dist: isolate-proto (>=0.3.1,<0.4.0)
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.1"
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 = "^0.3.1"
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,<0.25.0"
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
- boto3 = "^1.33.8"
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,<0.25.0',
64
- 'isolate-proto>=0.3.1,<0.4.0',
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.1',
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
- _get_agent_credentials,
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(["fastapi==0.99.1", "uvicorn"])
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 = _get_agent_credentials(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 ServeWrapper:
733
- _func: Callable
734
-
735
- def __init__(self, func: Callable):
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
- app = self.build_app()
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
- obj[f"x-fal-order-{key}"] = list(obj[key].keys())
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
- if "allOf" in schema:
800
- for sub_schema in schema["allOf"]:
801
- order_schema_object(sub_schema)
802
- if "properties" in schema:
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") or {}:
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]