fal 1.27.1__py3-none-any.whl → 1.28.1__py3-none-any.whl

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/_fal_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.27.1'
21
- __version_tuple__ = version_tuple = (1, 27, 1)
20
+ __version__ = version = '1.28.1'
21
+ __version_tuple__ = version_tuple = (1, 28, 1)
fal/api.py CHANGED
@@ -953,6 +953,11 @@ def function( # type: ignore
953
953
  ):
954
954
  if host is None:
955
955
  host = FalServerlessHost()
956
+
957
+ # NOTE: assuming kind="container" if image is provided
958
+ if config.get("image"):
959
+ kind = "container"
960
+
956
961
  options = host.parse_options(kind=kind, **config)
957
962
 
958
963
  def wrapper(func: Callable[ArgsT, ReturnT]):
fal/app.py CHANGED
@@ -411,28 +411,31 @@ class App(BaseServable):
411
411
  )
412
412
  return response
413
413
 
414
- @app.middleware("http")
415
- async def set_global_object_preference(request, call_next):
416
- try:
417
- preference_dict = request_lifecycle_preference(request)
418
- if preference_dict is not None:
419
- # This will not work properly for apps with multiplexing enabled
420
- # we may mix up the preferences between requests
421
- LIFECYCLE_PREFERENCE.set(preference_dict)
422
- except Exception:
423
- from fastapi.logger import logger
424
-
425
- logger.exception(
426
- "Failed set a global lifecycle preference %s",
427
- self.__class__.__name__,
428
- )
414
+ multiplexing = self.host_kwargs.get("max_multiplexing") or 1
415
+ if multiplexing == 1:
416
+ # just register the middleware if we are not multiplexing
417
+ @app.middleware("http")
418
+ async def set_global_object_preference(request, call_next):
419
+ try:
420
+ preference_dict = request_lifecycle_preference(request)
421
+ if preference_dict is not None:
422
+ # This will not work properly for apps with multiplexing enabled
423
+ # we may mix up the preferences between requests
424
+ LIFECYCLE_PREFERENCE.set(preference_dict)
425
+ except Exception:
426
+ from fastapi.logger import logger
427
+
428
+ logger.exception(
429
+ "Failed set a global lifecycle preference %s",
430
+ self.__class__.__name__,
431
+ )
429
432
 
430
- try:
431
- return await call_next(request)
432
- finally:
433
- # We may miss the global preference if there are operations
434
- # being done in the background that go beyond the request
435
- LIFECYCLE_PREFERENCE.set(None)
433
+ try:
434
+ return await call_next(request)
435
+ finally:
436
+ # We may miss the global preference if there are operations
437
+ # being done in the background that go beyond the request
438
+ LIFECYCLE_PREFERENCE.set(None)
436
439
 
437
440
  @app.middleware("http")
438
441
  async def set_request_id(request, call_next):
fal/cli/_utils.py CHANGED
@@ -12,7 +12,7 @@ def get_client(host: str, team: str | None = None):
12
12
 
13
13
  def is_app_name(app_ref: tuple[str, str | None]) -> bool:
14
14
  is_single_file = app_ref[1] is None
15
- is_python_file = app_ref[0].endswith(".py")
15
+ is_python_file = app_ref[0] is None or app_ref[0].endswith(".py")
16
16
 
17
17
  return is_single_file and not is_python_file
18
18
 
@@ -42,9 +42,16 @@ def get_app_data_from_toml(app_name):
42
42
 
43
43
  app_auth = app_data.pop("auth", "private")
44
44
  app_deployment_strategy = app_data.pop("deployment_strategy", "recreate")
45
- app_no_scale = app_data.pop("no_scale", False)
45
+
46
+ if "no_scale" in app_data:
47
+ # Deprecated
48
+ app_no_scale = app_data.pop("no_scale")
49
+ print("[WARNING] no_scale is deprecated, use app_scale_settings instead")
50
+ app_reset_scale = not app_no_scale
51
+ else:
52
+ app_reset_scale = app_data.pop("app_scale_settings", False)
46
53
 
47
54
  if len(app_data) > 0:
48
55
  raise ValueError(f"Found unexpected keys in pyproject.toml: {app_data}")
49
56
 
50
- return app_ref, app_auth, app_deployment_strategy, app_no_scale
57
+ return app_ref, app_auth, app_deployment_strategy, app_reset_scale
fal/cli/deploy.py CHANGED
@@ -66,9 +66,9 @@ def _deploy_from_reference(
66
66
  app_ref: Tuple[Optional[Union[Path, str]], ...],
67
67
  app_name: str,
68
68
  args,
69
- auth: Optional[Literal["public", "shared", "private"]] = None,
70
- deployment_strategy: Optional[Literal["recreate", "rolling"]] = None,
71
- scale: bool = True,
69
+ auth: Optional[Literal["public", "shared", "private"]],
70
+ deployment_strategy: Optional[Literal["recreate", "rolling"]],
71
+ scale: bool,
72
72
  ):
73
73
  from fal.api import FalServerlessError, FalServerlessHost
74
74
  from fal.utils import load_function_from
@@ -146,7 +146,7 @@ def _deploy(args):
146
146
  raise ValueError("Cannot use --app-name or --auth with app name reference.")
147
147
 
148
148
  app_name = args.app_ref[0]
149
- app_ref, app_auth, app_deployment_strategy, app_no_scale = (
149
+ app_ref, app_auth, app_deployment_strategy, app_scale_settings = (
150
150
  get_app_data_from_toml(app_name)
151
151
  )
152
152
  file_path, func_name = RefAction.split_ref(app_ref)
@@ -157,7 +157,7 @@ def _deploy(args):
157
157
  app_name = args.app_name
158
158
  app_auth = args.auth
159
159
  app_deployment_strategy = args.strategy
160
- app_no_scale = args.no_scale
160
+ app_scale_settings = args.app_scale_settings
161
161
 
162
162
  _deploy_from_reference(
163
163
  (file_path, func_name),
@@ -165,7 +165,7 @@ def _deploy(args):
165
165
  args,
166
166
  app_auth,
167
167
  app_deployment_strategy,
168
- scale=not app_no_scale,
168
+ scale=app_scale_settings,
169
169
  )
170
170
 
171
171
 
@@ -233,12 +233,17 @@ def add_parser(main_subparsers, parents):
233
233
  )
234
234
  parser.add_argument(
235
235
  "--no-scale",
236
+ action="store_false",
237
+ dest="app_scale_settings",
238
+ default=False,
239
+ help="Use the previous deployment of the application for scale settings. "
240
+ "This is the default behavior.",
241
+ )
242
+ parser.add_argument(
243
+ "--reset-scale",
236
244
  action="store_true",
237
- help=(
238
- "Use min_concurrency/max_concurrency/concurrency_buffer/max_multiplexing "
239
- "from previous deployment of application with this name, if exists. "
240
- "Otherwise will use the values from the application code."
241
- ),
245
+ dest="app_scale_settings",
246
+ help="Use the application code for scale settings.",
242
247
  )
243
248
 
244
249
  parser.set_defaults(func=_deploy)
fal/cli/profile.py CHANGED
@@ -68,12 +68,17 @@ def _unset(args, config: Config | None = None):
68
68
  def _key_set(args):
69
69
  config = Config(validate_profile=True)
70
70
 
71
+ args.console.print("Go to https://fal.ai/dashboard/keys to create a key.")
72
+ args.console.print(
73
+ "Enter the key in the format [cyan][bold]id:secret[/][/]. You can copy and paste it from the browser." # noqa: E501
74
+ )
75
+
71
76
  while True:
72
77
  key = input("Enter the key: ")
73
78
  if ":" in key:
74
79
  break
75
80
  args.console.print(
76
- "[red]Invalid key. The key must be in the format [bold]key:value[/].[/]"
81
+ "[yellow]Invalid key. The key must be in the format:[/] [cyan][bold]id:secret[/][/]." # noqa: E501
77
82
  )
78
83
 
79
84
  with config.edit():
@@ -99,10 +104,10 @@ def _create(args):
99
104
  with config.edit() as config:
100
105
  config._config[args.PROFILE] = {}
101
106
 
102
- args.console.print(f"Profile [cyan]{args.PROFILE}[/] created.")
103
107
  args.console.print(
104
- f"Use [bold]fal profile set {args.PROFILE}[/] to set it as default."
108
+ f"Profile [cyan]{args.PROFILE}[/] created. Setting it as default profile..."
105
109
  )
110
+ _set(args)
106
111
 
107
112
 
108
113
  def _delete(args):
@@ -184,7 +189,7 @@ def add_parser(main_subparsers, parents):
184
189
  )
185
190
  host_set_parser.set_defaults(func=_host_set)
186
191
 
187
- create_help = "Create a new profile. Use 'fal profile set <name>' to set it as default after creation." # noqa: E501
192
+ create_help = "Create a new profile."
188
193
  create_parser = subparsers.add_parser(
189
194
  "create",
190
195
  description=create_help,
fal/config.py CHANGED
@@ -6,7 +6,9 @@ from typing import Dict, Iterator, List, Optional
6
6
 
7
7
  SETTINGS_SECTION = "__internal__"
8
8
 
9
- NO_PROFILE_ERROR = ValueError("No profile set.")
9
+ NO_PROFILE_ERROR = ValueError(
10
+ "No profile set. Use command [bold]fal profile set[/] to set a profile."
11
+ )
10
12
 
11
13
 
12
14
  class Config:
fal/exceptions/auth.py CHANGED
@@ -7,5 +7,5 @@ class UnauthenticatedException(FalServerlessException):
7
7
  def __init__(self) -> None:
8
8
  super().__init__(
9
9
  "You must be authenticated. "
10
- "Login via `fal auth login` or make sure to setup fal keys correctly."
10
+ "Use [bold]fal auth login[/] or [bold]fal profile key[/] to set your fal key." # noqa: E501
11
11
  )
fal/toolkit/file/file.py CHANGED
@@ -201,9 +201,11 @@ class File(BaseModel):
201
201
 
202
202
  fdata = FileData(data, content_type, file_name)
203
203
 
204
- object_lifecycle_preference = (
205
- request_lifecycle_preference(request) or LIFECYCLE_PREFERENCE.get()
206
- )
204
+ if request:
205
+ object_lifecycle_preference = request_lifecycle_preference(request)
206
+ else:
207
+ object_lifecycle_preference = LIFECYCLE_PREFERENCE.get()
208
+
207
209
  save_kwargs.setdefault(
208
210
  "object_lifecycle_preference", object_lifecycle_preference
209
211
  )
@@ -250,9 +252,12 @@ class File(BaseModel):
250
252
  fallback_save_kwargs = fallback_save_kwargs or {}
251
253
 
252
254
  content_type = content_type or "application/octet-stream"
253
- object_lifecycle_preference = (
254
- request_lifecycle_preference(request) or LIFECYCLE_PREFERENCE.get()
255
- )
255
+
256
+ if request:
257
+ object_lifecycle_preference = request_lifecycle_preference(request)
258
+ else:
259
+ object_lifecycle_preference = LIFECYCLE_PREFERENCE.get()
260
+
256
261
  save_kwargs.setdefault(
257
262
  "object_lifecycle_preference", object_lifecycle_preference
258
263
  )
@@ -332,12 +337,9 @@ class CompressedFile(File):
332
337
  shutil.rmtree(self.extract_dir)
333
338
 
334
339
 
335
- def request_lifecycle_preference(request: Optional[Request]) -> dict[str, str] | None:
340
+ def request_lifecycle_preference(request: Request) -> dict[str, str] | None:
336
341
  import json
337
342
 
338
- if request is None:
339
- return None
340
-
341
343
  preference_str = request.headers.get(OBJECT_LIFECYCLE_PREFERENCE_KEY)
342
344
  if preference_str is None:
343
345
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.27.1
3
+ Version: 1.28.1
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <support@fal.ai>
6
6
  Requires-Python: >=3.8
@@ -1,12 +1,12 @@
1
1
  fal/__init__.py,sha256=wXs1G0gSc7ZK60-bHe-B2m0l_sA6TrFk4BxY0tMoLe8,784
2
2
  fal/__main__.py,sha256=4JMK66Wj4uLZTKbF-sT3LAxOsr6buig77PmOkJCRRxw,83
3
- fal/_fal_version.py,sha256=3iEnYoHxc4pb1GpjK8VvmamtxkSp2aFPQ3We7nMxWMQ,513
3
+ fal/_fal_version.py,sha256=uuctUYGLYKZiRv4u5GjbkTTYV9QJ7oYdTxJCsNX-iN8,513
4
4
  fal/_serialization.py,sha256=npXNsFJ5G7jzBeBIyVMH01Ww34mGY4XWhHpRbSrTtnQ,7598
5
5
  fal/_version.py,sha256=1BbTFnucNC_6ldKJ_ZoC722_UkW4S9aDBSW9L0fkKAw,2315
6
- fal/api.py,sha256=wIEt21P1C7U-dYQEcyHUxxuuTnvzFyTpWDoHoaxq7tg,47385
7
- fal/app.py,sha256=3RDFV6JUiUk8b3WJanKQYA-kW74t5bqaNy8OYuUH5-Q,25491
6
+ fal/api.py,sha256=TWUpQICgsRO5aDdRP8A3sFI26P6QM93TobcW9M4E0lQ,47501
7
+ fal/app.py,sha256=LktoVpMj3CXR3WCnptakDUvFwOVWsCfstl8wVofovIA,25740
8
8
  fal/apps.py,sha256=pzCd2mrKl5J_4oVc40_pggvPtFahXBCdrZXWpnaEJVs,12130
9
- fal/config.py,sha256=G3IOTLQuu_VAvzPFdx9v22NpJ9m4gU8qEHic7e7-WwU,3511
9
+ fal/config.py,sha256=1HRaOJFOAjB7fbQoEPCSH85gMvEEMIMPeupVWgrHVgU,3572
10
10
  fal/container.py,sha256=FTsa5hOW4ars-yV1lUoc0BNeIIvAZcpw7Ftyt3A4m_w,2000
11
11
  fal/files.py,sha256=iWkOqCtdoz7_V-wOvV1iB1zg9x7fdGRcmGGyBkCuFXI,7449
12
12
  fal/flags.py,sha256=QonyDM7R2GqfAB1bJr46oriu-fHJCkpUwXuSdanePWg,987
@@ -21,20 +21,20 @@ fal/auth/__init__.py,sha256=2mEKdk6_1GclF3cPC3uWSRKFf0KHNIUNAi0xYRbdJ1A,6278
21
21
  fal/auth/auth0.py,sha256=g5OgEKe4rsbkLQp6l7EauOAVL6WsmKjuA1wmzmyvvhc,5354
22
22
  fal/auth/local.py,sha256=sndkM6vKpeVny6NHTacVlTbiIFqaksOmw0Viqs_RN1U,1790
23
23
  fal/cli/__init__.py,sha256=padK4o0BFqq61kxAA1qQ0jYr2SuhA2mf90B3AaRkmJA,37
24
- fal/cli/_utils.py,sha256=anFfy6qouB8QzH0Yho41GulGiJu3q1KKIwgyVQCzgRQ,1593
24
+ fal/cli/_utils.py,sha256=ulYezhr3G29nTIF8MDQ6tsW01Oj1zPo-YSqMoBi05Ic,1871
25
25
  fal/cli/api.py,sha256=ZuDE_PIC-czzneTAWMwvC7P7WnwIyluNZSuJqzCFhqI,2640
26
26
  fal/cli/apps.py,sha256=q0uxuD4h_5tft77QfyFbDz0kGmM51JT39du_OeFytPA,10565
27
27
  fal/cli/auth.py,sha256=Qe-Z3ycXJnOzHimz5PjCQYoni8MF4csmdL19yGN7a1o,5171
28
28
  fal/cli/cli_nested_json.py,sha256=veSZU8_bYV3Iu1PAoxt-4BMBraNIqgH5nughbs2UKvE,13539
29
29
  fal/cli/create.py,sha256=a8WDq-nJLFTeoIXqpb5cr7GR7YR9ZZrQCawNm34KXXE,627
30
30
  fal/cli/debug.py,sha256=u_urnyFzSlNnrq93zz_GXE9FX4VyVxDoamJJyrZpFI0,1312
31
- fal/cli/deploy.py,sha256=D47AR_voAy-r-h8xnVHICLsosyFRCW4Bn7Br7xwwjNg,7785
31
+ fal/cli/deploy.py,sha256=TseHLukhcHbtfnUMFqd2gQ1eNlBr58l_-y61ZdRsshw,7886
32
32
  fal/cli/doctor.py,sha256=U4ne9LX5gQwNblsYQ27XdO8AYDgbYjTO39EtxhwexRM,983
33
33
  fal/cli/files.py,sha256=pSgAnTm2eHdP-IPkMIVfnK_Ii7mkSSOVgvbsiFUVBC0,2936
34
34
  fal/cli/keys.py,sha256=7Sf4DT4le89G42eAOt0ltRjbZAtE70AVQ62hmjZhUy0,3059
35
35
  fal/cli/main.py,sha256=LnJCe83Fdr5-i3Fkpvrd9BZPjHtcX_H1EEmy6rVlCz8,3180
36
36
  fal/cli/parser.py,sha256=jYsGQ0BLQuKI7KtN1jnLVYKMbLtez7hPjwTNfG3UPSk,2964
37
- fal/cli/profile.py,sha256=9Iz5Prk2mR2JmxBuNPcKAcQJVHlGGXIlKh_7MXDl7U4,5879
37
+ fal/cli/profile.py,sha256=agyGFQRLHOtzJEdN3Pn9317rg044XaszNCZrf0ELK-8,6016
38
38
  fal/cli/run.py,sha256=nAC12Qss4Fg1XmV0qOS9RdGNLYcdoHeRgQMvbTN4P9I,1202
39
39
  fal/cli/runners.py,sha256=7efNX9vm6D1aBlg0M5-u5plw3HHC41Sj-N7eRNIHnqw,3689
40
40
  fal/cli/secrets.py,sha256=QKSmazu-wiNF6fOpGL9v2TDYxAjX9KTi7ot7vnv6f5E,2474
@@ -45,7 +45,7 @@ fal/console/ux.py,sha256=KMQs3UHQvVHDxDQQqlot-WskVKoMQXOE3jiVkkfmIMY,356
45
45
  fal/exceptions/__init__.py,sha256=m2okJEpax11mnwmoqO_pCGtbt-FvzKiiuMhKo2ok-_8,270
46
46
  fal/exceptions/_base.py,sha256=LwzpMaW_eYQEC5s26h2qGXbNA-S4bOqC8s-bMCX6HjE,1491
47
47
  fal/exceptions/_cuda.py,sha256=L3qvDNaPTthp95IFSBI6pMt3YbRfn1H0inQkj_7NKF8,1719
48
- fal/exceptions/auth.py,sha256=gxRago5coI__vSIcdcsqhhq1lRPkvCnwPAueIaXTAdw,329
48
+ fal/exceptions/auth.py,sha256=fHea3SIeguInJVB5M33IuP4I5e_pVEifck1C_XJTYvc,351
49
49
  fal/logging/__init__.py,sha256=avgKA2V8GJeUtZuWZJjSYgkkrXKpuDMS-YiIBmLda7w,1599
50
50
  fal/logging/isolate.py,sha256=jIryi46ZVlJ1mfan4HLNQQ3jwMi8z-WwfqqLlttQVkc,2449
51
51
  fal/logging/style.py,sha256=ckIgHzvF4DShM5kQh8F133X53z_vF46snuDHVmo_h9g,386
@@ -59,7 +59,7 @@ fal/toolkit/types.py,sha256=kkbOsDKj1qPGb1UARTBp7yuJ5JUuyy7XQurYUBCdti8,4064
59
59
  fal/toolkit/audio/__init__.py,sha256=sqNVfrKbppWlIGLoFTaaNTxLpVXsFHxOSHLA5VG547A,35
60
60
  fal/toolkit/audio/audio.py,sha256=gt458h989iQ-EhQSH-mCuJuPBY4RneLJE05f_QWU1E0,572
61
61
  fal/toolkit/file/__init__.py,sha256=FbNl6wD-P0aSSTUwzHt4HujBXrbC3ABmaigPQA4hRfg,70
62
- fal/toolkit/file/file.py,sha256=wDKK91i_1NJD5GGVSJPhFQ8C2FgxG2bJUnUexpHTjt8,10782
62
+ fal/toolkit/file/file.py,sha256=0VI8ZgGV3tnbDN6vlM8L8AHt5gul6-emuPLjVBB_vt0,10836
63
63
  fal/toolkit/file/types.py,sha256=MMAH_AyLOhowQPesOv1V25wB4qgbJ3vYNlnTPbdSv1M,2304
64
64
  fal/toolkit/file/providers/fal.py,sha256=Ph8v3Cm_eFu1b1AXiPKZQ5r8AWUALD3Wk18uw3z8RDQ,46910
65
65
  fal/toolkit/file/providers/gcp.py,sha256=DKeZpm1MjwbvEsYvkdXUtuLIJDr_UNbqXj_Mfv3NTeo,2437
@@ -142,8 +142,8 @@ openapi_fal_rest/models/workflow_node_type.py,sha256=-FzyeY2bxcNmizKbJI8joG7byRi
142
142
  openapi_fal_rest/models/workflow_schema.py,sha256=4K5gsv9u9pxx2ItkffoyHeNjBBYf6ur5bN4m_zePZNY,2019
143
143
  openapi_fal_rest/models/workflow_schema_input.py,sha256=2OkOXWHTNsCXHWS6EGDFzcJKkW5FIap-2gfO233EvZQ,1191
144
144
  openapi_fal_rest/models/workflow_schema_output.py,sha256=EblwSPAGfWfYVWw_WSSaBzQVju296is9o28rMBAd0mc,1196
145
- fal-1.27.1.dist-info/METADATA,sha256=TKeydxAK7aqE2EBQHAruwEtCBS11OjqDTQx_R3PQNW0,4089
146
- fal-1.27.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
147
- fal-1.27.1.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
148
- fal-1.27.1.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
149
- fal-1.27.1.dist-info/RECORD,,
145
+ fal-1.28.1.dist-info/METADATA,sha256=kcAdoePxY_Zy3_-5QOvvaS5P72vGwa-db_3p8WktQCU,4089
146
+ fal-1.28.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
147
+ fal-1.28.1.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
148
+ fal-1.28.1.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
149
+ fal-1.28.1.dist-info/RECORD,,
File without changes