fal 1.50.1__py3-none-any.whl → 1.57.2__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.
@@ -0,0 +1,220 @@
1
+ """PyTorch compilation cache management utilities.
2
+
3
+ This module provides utilities for managing PyTorch Inductor compilation caches
4
+ across workers. When using torch.compile(), PyTorch generates optimized CUDA kernels
5
+ on first run, which can take 20-30 seconds. By sharing these compiled kernels across
6
+ workers, subsequent workers can load pre-compiled kernels in ~2 seconds instead of
7
+ recompiling.
8
+
9
+ Typical usage in a model setup:
10
+
11
+ Manual cache management:
12
+ dir_hash = load_inductor_cache("mymodel/v1")
13
+ self.model = torch.compile(self.model)
14
+ self.warmup() # Triggers compilation
15
+ sync_inductor_cache("mymodel/v1", dir_hash)
16
+
17
+ Context manager (automatic):
18
+ with synchronized_inductor_cache("mymodel/v1"):
19
+ self.model = torch.compile(self.model)
20
+ self.warmup() # Compilation is automatically synced after
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import hashlib
26
+ import os
27
+ import re
28
+ import shutil
29
+ import subprocess
30
+ import tempfile
31
+ from collections.abc import Iterator
32
+ from contextlib import contextmanager
33
+ from pathlib import Path
34
+
35
+ LOCAL_INDUCTOR_CACHE_DIR = Path("/tmp/inductor-cache/")
36
+ GLOBAL_INDUCTOR_CACHES_DIR = Path("/data/inductor-caches/")
37
+ PERSISTENT_TMP_DIR = Path("/data/tmp/")
38
+
39
+
40
+ def get_gpu_type() -> str:
41
+ """Detect the GPU type using nvidia-smi.
42
+
43
+ Returns:
44
+ The GPU model name (e.g., "H100", "A100", "H200") or "UNKNOWN"
45
+ if detection fails.
46
+
47
+ Example:
48
+ >>> gpu_type = get_gpu_type()
49
+ >>> print(f"Running on: {gpu_type}")
50
+ Running on: H100
51
+ """
52
+ try:
53
+ gpu_type_string = subprocess.run(
54
+ ["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"],
55
+ capture_output=True,
56
+ text=True,
57
+ check=False,
58
+ ).stdout
59
+ matches = re.search(r"NVIDIA [a-zA-Z0-9]*", gpu_type_string)
60
+ # check for matches - if there are none, return "UNKNOWN"
61
+ if matches:
62
+ gpu_type = matches.group(0)
63
+ return gpu_type[7:] # remove `NVIDIA `
64
+ else:
65
+ return "UNKNOWN"
66
+ except Exception:
67
+ return "UNKNOWN"
68
+
69
+
70
+ def _dir_hash(path: Path) -> str:
71
+ """Compute a hash of all filenames in a directory (recursively).
72
+
73
+ Args:
74
+ path: Directory to hash.
75
+
76
+ Returns:
77
+ SHA256 hex digest of sorted filenames.
78
+ """
79
+ # Hash of all the filenames in the directory, recursively, sorted
80
+ filenames = {str(file) for file in path.rglob("*") if file.is_file()}
81
+ return hashlib.sha256("".join(sorted(filenames)).encode()).hexdigest()
82
+
83
+
84
+ def load_inductor_cache(cache_key: str) -> str:
85
+ """Load PyTorch Inductor compilation cache from global storage.
86
+
87
+ This function:
88
+ 1. Sets TORCHINDUCTOR_CACHE_DIR environment variable
89
+ 2. Looks for cached compiled kernels in GPU-specific global storage
90
+ 3. Unpacks the cache to local temporary directory
91
+ 4. Returns a hash of the unpacked directory for change detection
92
+
93
+ Args:
94
+ cache_key: Unique identifier for this cache (e.g., "flux/2", "mymodel/v1")
95
+
96
+ Returns:
97
+ Hash of the unpacked cache directory, or empty string if cache not found.
98
+
99
+ Example:
100
+ >>> dir_hash = load_inductor_cache("flux/2")
101
+ Found compilation cache at /data/inductor-caches/H100/flux/2.zip, unpacking...
102
+ Cache unpacked successfully.
103
+ """
104
+ gpu_type = get_gpu_type()
105
+
106
+ os.environ["TORCHINDUCTOR_CACHE_DIR"] = str(LOCAL_INDUCTOR_CACHE_DIR)
107
+
108
+ cache_source_path = GLOBAL_INDUCTOR_CACHES_DIR / gpu_type / f"{cache_key}.zip"
109
+
110
+ try:
111
+ next(cache_source_path.parent.iterdir(), None)
112
+ except Exception as e:
113
+ # Check for cache without gpu_type in the path
114
+ try:
115
+ old_source_path = GLOBAL_INDUCTOR_CACHES_DIR / f"{cache_key}.zip"
116
+ # Since old source exists, copy it over to global caches
117
+ os.makedirs(cache_source_path.parent, exist_ok=True)
118
+ shutil.copy(old_source_path, cache_source_path)
119
+ except Exception:
120
+ print(f"Failed to list: {e}")
121
+
122
+ if not cache_source_path.exists():
123
+ print(f"Couldn't find compilation cache at {cache_source_path}")
124
+ return ""
125
+
126
+ print(f"Found compilation cache at {cache_source_path}, unpacking...")
127
+ try:
128
+ shutil.unpack_archive(cache_source_path, LOCAL_INDUCTOR_CACHE_DIR)
129
+ except Exception as e:
130
+ print(f"Failed to unpack cache: {e}")
131
+ return ""
132
+
133
+ print("Cache unpacked successfully.")
134
+ return _dir_hash(LOCAL_INDUCTOR_CACHE_DIR)
135
+
136
+
137
+ def sync_inductor_cache(cache_key: str, unpacked_dir_hash: str) -> None:
138
+ """Sync updated PyTorch Inductor cache back to global storage.
139
+
140
+ This function:
141
+ 1. Checks if the local cache has changed (by comparing hashes)
142
+ 2. If changed, creates a zip archive of the new cache
143
+ 3. Saves it to GPU-specific global storage
144
+
145
+ Args:
146
+ cache_key: Unique identifier for this cache (same as used in
147
+ load_inductor_cache)
148
+ unpacked_dir_hash: Hash returned from load_inductor_cache
149
+ (for change detection)
150
+
151
+ Example:
152
+ >>> sync_inductor_cache("flux/2", dir_hash)
153
+ No changes in the cache dir, skipping sync.
154
+ # or
155
+ Changes detected in the cache dir, syncing...
156
+ """
157
+ gpu_type = get_gpu_type()
158
+ if not LOCAL_INDUCTOR_CACHE_DIR.exists():
159
+ print(f"No cache to sync, {LOCAL_INDUCTOR_CACHE_DIR} doesn't exist.")
160
+ return
161
+
162
+ if not GLOBAL_INDUCTOR_CACHES_DIR.exists():
163
+ GLOBAL_INDUCTOR_CACHES_DIR.mkdir(parents=True)
164
+
165
+ # If we updated the cache (the hashes of LOCAL_INDUCTOR_CACHE_DIR and
166
+ # unpacked_dir_hash differ), we pack the cache and move it to the
167
+ # global cache directory.
168
+ new_dir_hash = _dir_hash(LOCAL_INDUCTOR_CACHE_DIR)
169
+ if new_dir_hash == unpacked_dir_hash:
170
+ print("No changes in the cache dir, skipping sync.")
171
+ return
172
+
173
+ print("Changes detected in the cache dir, syncing...")
174
+ os.makedirs(
175
+ PERSISTENT_TMP_DIR, exist_ok=True
176
+ ) # Non fal-ai users do not have this directory
177
+ with tempfile.TemporaryDirectory(dir=PERSISTENT_TMP_DIR) as temp_dir:
178
+ temp_dir_path = Path(temp_dir)
179
+ cache_path = GLOBAL_INDUCTOR_CACHES_DIR / gpu_type / f"{cache_key}.zip"
180
+ cache_path.parent.mkdir(parents=True, exist_ok=True)
181
+
182
+ try:
183
+ zip_name = shutil.make_archive(
184
+ str(temp_dir_path / "inductor_cache"),
185
+ "zip",
186
+ LOCAL_INDUCTOR_CACHE_DIR,
187
+ )
188
+ os.rename(
189
+ zip_name,
190
+ cache_path,
191
+ )
192
+ except Exception as e:
193
+ print(f"Failed to sync cache: {e}")
194
+ return
195
+
196
+
197
+ @contextmanager
198
+ def synchronized_inductor_cache(cache_key: str) -> Iterator[None]:
199
+ """Context manager to automatically load and sync PyTorch Inductor cache.
200
+
201
+ This wraps load_inductor_cache and sync_inductor_cache for convenience.
202
+ The cache is loaded on entry and synced on exit (even if an exception occurs).
203
+
204
+ Args:
205
+ cache_key: Unique identifier for this cache (e.g., "flux/2", "mymodel/v1")
206
+
207
+ Yields:
208
+ None
209
+
210
+ Example:
211
+ >>> with synchronized_inductor_cache("mymodel/v1"):
212
+ ... self.model = torch.compile(self.model)
213
+ ... self.warmup() # Compilation happens here
214
+ # Cache is automatically synced after the with block
215
+ """
216
+ unpacked_dir_hash = load_inductor_cache(cache_key)
217
+ try:
218
+ yield
219
+ finally:
220
+ sync_inductor_cache(cache_key, unpacked_dir_hash)
fal/toolkit/file/file.py CHANGED
@@ -16,8 +16,7 @@ from fastapi import Request
16
16
  if not hasattr(pydantic, "__version__") or pydantic.__version__.startswith("1."):
17
17
  IS_PYDANTIC_V2 = False
18
18
  else:
19
- from pydantic import GetCoreSchemaHandler
20
- from pydantic_core import CoreSchema, core_schema
19
+ from pydantic import model_validator
21
20
 
22
21
  IS_PYDANTIC_V2 = True
23
22
 
@@ -137,14 +136,16 @@ class File(BaseModel):
137
136
  # Pydantic custom validator for input type conversion
138
137
  if IS_PYDANTIC_V2:
139
138
 
139
+ @model_validator(mode="before")
140
140
  @classmethod
141
- def __get_pydantic_core_schema__(
142
- cls, source_type: Any, handler: GetCoreSchemaHandler
143
- ) -> CoreSchema:
144
- return core_schema.no_info_before_validator_function(
145
- cls.__convert_from_str,
146
- handler(source_type),
147
- )
141
+ def __convert_from_str_v2(cls, value: Any):
142
+ if isinstance(value, str):
143
+ parsed_url = urlparse(value)
144
+ if parsed_url.scheme not in ["http", "https", "data"]:
145
+ raise ValueError("value must be a valid URL")
146
+ # Return a mapping so the model can be constructed normally
147
+ return {"url": parsed_url.geturl()}
148
+ return value
148
149
 
149
150
  else:
150
151
 
fal/utils.py CHANGED
@@ -1,11 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING
4
5
 
5
- import fal._serialization
6
- from fal import App, wrap_app
7
-
8
- from .api import FalServerlessError, FalServerlessHost, IsolatedFunction
6
+ if TYPE_CHECKING:
7
+ from .api import FalServerlessHost, IsolatedFunction
9
8
 
10
9
 
11
10
  @dataclass
@@ -17,6 +16,62 @@ class LoadedFunction:
17
16
  source_code: str | None
18
17
 
19
18
 
19
+ def _find_target(
20
+ module: dict[str, object], function_name: str | None = None
21
+ ) -> tuple[object, str | None, str | None]:
22
+ import fal
23
+ from fal.api import FalServerlessError, IsolatedFunction
24
+
25
+ if function_name is not None:
26
+ if function_name not in module:
27
+ raise FalServerlessError(f"Function '{function_name}' not found in module")
28
+
29
+ target = module[function_name]
30
+
31
+ if isinstance(target, type) and issubclass(target, fal.App):
32
+ return target, target.app_name, target.app_auth
33
+
34
+ if isinstance(target, IsolatedFunction):
35
+ return target, function_name, None
36
+
37
+ raise FalServerlessError(
38
+ f"Function '{function_name}' is not a fal.App or a fal.function"
39
+ )
40
+
41
+ fal_apps = {
42
+ obj_name: obj
43
+ for obj_name, obj in module.items()
44
+ if isinstance(obj, type) and issubclass(obj, fal.App) and obj is not fal.App
45
+ }
46
+
47
+ if len(fal_apps) == 1:
48
+ [(function_name, target)] = fal_apps.items()
49
+ return target, target.app_name, target.app_auth
50
+ elif len(fal_apps) > 1:
51
+ raise FalServerlessError(
52
+ f"Multiple fal.Apps found in the module: {list(fal_apps.keys())}. "
53
+ "Please specify the name of the app."
54
+ )
55
+
56
+ fal_functions = {
57
+ obj_name: obj
58
+ for obj_name, obj in module.items()
59
+ if isinstance(obj, IsolatedFunction)
60
+ }
61
+
62
+ if len(fal_functions) == 0:
63
+ raise FalServerlessError("No fal.App or fal.function found in the module.")
64
+ elif len(fal_functions) > 1:
65
+ raise FalServerlessError(
66
+ "Multiple fal.functions found in the module: "
67
+ f"{list(fal_functions.keys())}. "
68
+ "Please specify the name of the function."
69
+ )
70
+
71
+ [(function_name, target)] = fal_functions.items()
72
+ return target, function_name, None
73
+
74
+
20
75
  def load_function_from(
21
76
  host: FalServerlessHost,
22
77
  file_path: str,
@@ -26,45 +81,24 @@ def load_function_from(
26
81
  import runpy
27
82
  import sys
28
83
 
84
+ import fal._serialization
85
+ from fal import App, wrap_app
86
+
87
+ from .api import FalServerlessError, IsolatedFunction
88
+
29
89
  sys.path.append(os.getcwd())
30
90
  module = runpy.run_path(file_path)
31
- if function_name is None:
32
- fal_objects = {
33
- obj_name: obj
34
- for obj_name, obj in module.items()
35
- if isinstance(obj, type) and issubclass(obj, fal.App) and obj is not fal.App
36
- }
37
- if len(fal_objects) == 0:
38
- raise FalServerlessError("No fal.App found in the module.")
39
- elif len(fal_objects) > 1:
40
- raise FalServerlessError(
41
- "Multiple fal.Apps found in the module. "
42
- "Please specify the name of the app."
43
- )
44
-
45
- [(function_name, obj)] = fal_objects.items()
46
- app_name = obj.app_name
47
- app_auth = obj.app_auth
48
- else:
49
- app_name = None
50
- app_auth = None
51
-
52
- if function_name not in module:
53
- raise FalServerlessError(f"Function '{function_name}' not found in module")
91
+ target, app_name, app_auth = _find_target(module, function_name)
54
92
 
55
93
  # The module for the function is set to <run_path> when runpy is used, in which
56
94
  # case we want to manually include the package it is defined in.
57
95
  fal._serialization.include_package_from_path(file_path)
58
96
 
59
- target = module[function_name]
60
-
61
97
  with open(file_path) as f:
62
98
  source_code = f.read()
63
99
 
64
100
  endpoints = ["/"]
65
101
  if isinstance(target, type) and issubclass(target, App):
66
- app_name = target.app_name
67
- app_auth = target.app_auth
68
102
  endpoints = target.get_endpoints() or ["/"]
69
103
  target = wrap_app(target, host=host)
70
104
 
fal/workflows.py CHANGED
@@ -19,7 +19,6 @@ from rich.syntax import Syntax
19
19
  import fal
20
20
  from fal import flags
21
21
  from fal.exceptions import FalServerlessException
22
- from fal.rest_client import REST_CLIENT
23
22
 
24
23
  JSONType = Union[Dict[str, Any], List[Any], str, int, float, bool, None, "Leaf"]
25
24
  SchemaType = Dict[str, Any]
@@ -372,6 +371,11 @@ class Workflow:
372
371
  to_dict = to_json
373
372
 
374
373
  def publish(self, title: str, *, is_public: bool = True):
374
+ from fal.api.client import SyncServerlessClient
375
+
376
+ client = SyncServerlessClient()
377
+ rest_client = client._create_rest_client()
378
+
375
379
  workflow_contents = publish_workflow.TypedWorkflow(
376
380
  name=self.name,
377
381
  title=title,
@@ -379,7 +383,7 @@ class Workflow:
379
383
  is_public=is_public,
380
384
  )
381
385
  published_workflow = publish_workflow.sync(
382
- client=REST_CLIENT,
386
+ client=rest_client,
383
387
  json_body=workflow_contents,
384
388
  )
385
389
  if isinstance(published_workflow, Exception):
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.50.1
3
+ Version: 1.57.2
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
7
7
  Description-Content-Type: text/markdown
8
- Requires-Dist: isolate[build]<0.21.0,>=0.18.0
9
- Requires-Dist: isolate-proto<0.23.0,>=0.22.0
8
+ Requires-Dist: isolate[build]<0.22.0,>=0.18.0
9
+ Requires-Dist: isolate-proto>=0.27.0
10
10
  Requires-Dist: grpcio<2,>=1.64.0
11
11
  Requires-Dist: dill==0.3.7
12
12
  Requires-Dist: cloudpickle==3.0.0
@@ -21,8 +21,8 @@ Requires-Dist: rich<15,>=13.3.2
21
21
  Requires-Dist: rich_argparse
22
22
  Requires-Dist: packaging>=21.3
23
23
  Requires-Dist: pathspec<1,>=0.11.1
24
- Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<2.11
25
- Requires-Dist: fastapi<1,>=0.99.1
24
+ Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*
25
+ Requires-Dist: fastapi<0.119,>=0.99.1
26
26
  Requires-Dist: starlette-exporter>=0.21.0
27
27
  Requires-Dist: httpx>=0.15.4
28
28
  Requires-Dist: httpx-sse
@@ -60,7 +60,7 @@ Requires-Dist: fal[docs,test]; extra == "dev"
60
60
  Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
61
61
 
62
62
  [![PyPI](https://img.shields.io/pypi/v/fal.svg?logo=PyPI)](https://pypi.org/project/fal)
63
- [![Tests](https://img.shields.io/github/actions/workflow/status/fal-ai/fal/integration_tests.yaml?label=Tests)](https://github.com/fal-ai/fal/actions)
63
+ [![Tests](https://img.shields.io/github/actions/workflow/status/fal-ai/fal/fal-unit-tests.yml?label=Tests)](https://github.com/fal-ai/fal/actions)
64
64
 
65
65
  # fal
66
66
 
@@ -1,50 +1,51 @@
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=LL1Yx-cpQ3cud6IHC9ya0kDMPSCBaY6e8nDAG_QIwYY,706
3
+ fal/_fal_version.py,sha256=i0cW1NjyUIFsXnQhKIsnmK05Q1c85OjmjaQwpPYgb-Q,706
4
4
  fal/_serialization.py,sha256=2hPQhinTWinTTs2gDjPG6SxVCwkL_i6S8TfOSoCqLUs,7626
5
5
  fal/_version.py,sha256=1BbTFnucNC_6ldKJ_ZoC722_UkW4S9aDBSW9L0fkKAw,2315
6
- fal/app.py,sha256=mW7H2k41y4-BTEyvVmwTMEfVy2a5_ctAHTZEtQCn2ig,31061
6
+ fal/app.py,sha256=B5U5NlTT9CczOuTTTbwGZA5PEQEPheed9Bn5N6lS3PE,32244
7
7
  fal/apps.py,sha256=pzCd2mrKl5J_4oVc40_pggvPtFahXBCdrZXWpnaEJVs,12130
8
8
  fal/config.py,sha256=1HRaOJFOAjB7fbQoEPCSH85gMvEEMIMPeupVWgrHVgU,3572
9
9
  fal/container.py,sha256=FTsa5hOW4ars-yV1lUoc0BNeIIvAZcpw7Ftyt3A4m_w,2000
10
10
  fal/file_sync.py,sha256=7urM-wEzijTJMddnprkq5wyGPS09Ywdk4UoWWCL9CTA,11977
11
- fal/files.py,sha256=9hA7mC3Xm794I-P2_YMf0QRebrnBIDz_kUnUd4O3BiQ,7904
11
+ fal/files.py,sha256=hNHpOApfrtUyqAngykCQWG0Yqx4p1XWHu7ZnD83oWRQ,8522
12
12
  fal/flags.py,sha256=QonyDM7R2GqfAB1bJr46oriu-fHJCkpUwXuSdanePWg,987
13
13
  fal/project.py,sha256=QgfYfMKmNobMPufrAP_ga1FKcIAlSbw18Iar1-0qepo,2650
14
14
  fal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- fal/rest_client.py,sha256=kGBGmuyHfX1lR910EoKCYPjsyU8MdXawT_cW2q8Sajc,568
16
- fal/sdk.py,sha256=ksxXaSKzsK5KehDsmPzuhh-RX6kJazttuAY41NN-kqQ,29669
17
- fal/sync.py,sha256=ZuIJA2-hTPNANG9B_NNJZUsO68EIdTH0dc9MzeVE2VU,4340
18
- fal/utils.py,sha256=GSDJFdGBM7MtjkWIZW-VHcA6T99nZVYcRClLcoKNHuk,2309
19
- fal/workflows.py,sha256=Zl4f6Bs085hY40zmqScxDUyCu7zXkukDbW02iYOLTTI,14805
15
+ fal/sdk.py,sha256=kBO861eiQirMmECBQizBZH9aztPnNJ2j6FEJBedPiU8,30102
16
+ fal/sync.py,sha256=z0RbyQxbMo_zEHcP3g_R5J42GZaQOyw3XT570-eal74,4576
17
+ fal/utils.py,sha256=dzVesc1S6W2UVqORxxp0j7Z7gR1aTMgJZYji1t0NQN0,3403
18
+ fal/workflows.py,sha256=UMxhgT274MigYd93GM4sv7WA_D-fulWOJ0hUQn-2CRo,14914
20
19
  fal/api/__init__.py,sha256=dcPgWqbf3xA0BSv1rKsZMOmIyZeHjiIwTrGLYQHnhjI,88
21
- fal/api/api.py,sha256=m67EqjDcCiCHFdBDkVpCqFEgiW2panqKXay-JZxpXzo,51940
22
- fal/api/apps.py,sha256=YSy2RUvFGBmXp8OrtWf49FMLvzwS79e0LD426naaMx0,2219
23
- fal/api/client.py,sha256=KCHZ6_Eom5j4n0hAujsGBjjXaOySg1KBh3dk0V8Qaw8,4141
24
- fal/api/deploy.py,sha256=1HpvVbdKxJHXjq6Y7KzSHTKfe_MYDs_a2vR5tKMAykA,6447
20
+ fal/api/api.py,sha256=Zh4Cs484SEcAeJhZ7z6XmfWotmrosG1yl-QpEDAxf9c,53108
21
+ fal/api/apps.py,sha256=ynVXDH2e_OhyGubEniOqkH7JfBS2GP2GhVgk-zpgCf4,2819
22
+ fal/api/client.py,sha256=QDJioggQf_D3pdoKepAHCYkuFhorJxNkbc-XnFn-cCY,6135
23
+ fal/api/deploy.py,sha256=2kagmtA3gjVRQcZ5k09vUUfDGGG6mUhi6Ck03pdyGoI,6069
24
+ fal/api/keys.py,sha256=uIq2aQgtKjEQsp1NBtrFLK6mEYBf6WzdxBq8CCLIz3U,977
25
25
  fal/api/runners.py,sha256=QE7SnczoIC5ZEmeTFShkhaSFOMMkHBHrt-3y_IuFruE,878
26
+ fal/api/secrets.py,sha256=kB6deYY1qyjfri0TT1rBJUBeg72yRNuysza6QJROpIw,915
26
27
  fal/auth/__init__.py,sha256=mtyQou8DGHC-COjW9WbtRyyzjyt7fMlhVmsB4U-CBh4,6509
27
28
  fal/auth/auth0.py,sha256=g5OgEKe4rsbkLQp6l7EauOAVL6WsmKjuA1wmzmyvvhc,5354
28
29
  fal/auth/local.py,sha256=sndkM6vKpeVny6NHTacVlTbiIFqaksOmw0Viqs_RN1U,1790
29
30
  fal/cli/__init__.py,sha256=padK4o0BFqq61kxAA1qQ0jYr2SuhA2mf90B3AaRkmJA,37
30
- fal/cli/_utils.py,sha256=XwYoJr8SahaKB9OkGkw178FBpSeFAB-GqDXUQgGoFRE,2196
31
- fal/cli/api.py,sha256=ZuDE_PIC-czzneTAWMwvC7P7WnwIyluNZSuJqzCFhqI,2640
32
- fal/cli/apps.py,sha256=fKL5tdi2BSmnv0b1YMRASCinZ1-aH4j10ioaP0l8iMA,12529
31
+ fal/cli/_utils.py,sha256=UaL7o6wCXxOXks3Yyx02SQdkZj2zm0JvjS02Bxv7gjY,2328
32
+ fal/cli/api.py,sha256=EPh1RjFW-b5hf-T4FIjaP0wARdwGez4E5o1Qc3aYgSQ,2665
33
+ fal/cli/apps.py,sha256=GMsC_mlzzND65iTv4h-x7cVKcPTHcH0GDXAy-c92nJQ,14228
33
34
  fal/cli/auth.py,sha256=ZLjxuF4LobETJ2CLGMj_QurE0PiJxzKdFJZkux8uLHM,5977
34
35
  fal/cli/cli_nested_json.py,sha256=veSZU8_bYV3Iu1PAoxt-4BMBraNIqgH5nughbs2UKvE,13539
35
36
  fal/cli/create.py,sha256=a8WDq-nJLFTeoIXqpb5cr7GR7YR9ZZrQCawNm34KXXE,627
36
37
  fal/cli/debug.py,sha256=mTCjSpEZaNKcX225VZtry-BspFKSHURUuxUFuX6x5Cc,1488
37
- fal/cli/deploy.py,sha256=wTpMn1J9X8fytfeLKvJjwLN8sqcPKzWG0loin8ymqZ4,3890
38
+ fal/cli/deploy.py,sha256=IBh9cxyfDSczIB8SatJ1uo2TxxpMg4F7uL2D7_nzpx8,4387
38
39
  fal/cli/doctor.py,sha256=8SZrYG9Ku0F6LLUHtFdKopdIgZfFkw5E3Mwrxa9KOSk,1613
39
- fal/cli/files.py,sha256=8p_xqJJaimC3ezrw-d8Ybxb21iGC2DgRFyCF1nGhpPY,3855
40
- fal/cli/keys.py,sha256=iQVMr3WT8CUqSQT3qeCCiy6rRwoux9F-UEaC4bCwMWo,3754
40
+ fal/cli/files.py,sha256=s2Q1-kQqVJAlc33kVgF9nnDLScEz5TaravJ1ZaQh3G8,3755
41
+ fal/cli/keys.py,sha256=sGVAUsUnq8p_HN26GPPnBYVpOYxmGS6QZMc-bHu-bGE,3555
41
42
  fal/cli/main.py,sha256=LDy3gze9TRsvGa4uSNc8NMFmWMLpsyoC-msteICNiso,3371
42
43
  fal/cli/parser.py,sha256=siSY1kxqczZIs3l_jLwug_BpVzY_ZqHpewON3am83Ow,6658
43
44
  fal/cli/profile.py,sha256=PAY_ffifCT71VJ8VxfDVaXPT0U1oN8drvWZDFRXwvek,6678
44
- fal/cli/queue.py,sha256=YhcD0URGeqaJXqTl1hPEV9-UqdWsQ-4beG5xE9AGLJI,2727
45
- fal/cli/run.py,sha256=l3wq-Fa3bCznKrdsC-4ouAU_74rAjrb-SqRCWEuGJQM,1432
46
- fal/cli/runners.py,sha256=JaqSQWQXRjM3vatllLUp8KnfElhUpih9kyPCuaHUedY,20980
47
- fal/cli/secrets.py,sha256=HfIeO2IZpCEiBC6Cs5Kpi3zckfDnc7GsLwLdgj3NnPU,3085
45
+ fal/cli/queue.py,sha256=zfBP-ILm4lNiKowOEBtKqcE6Qus7gSl-5Q6CLOTMYm0,2948
46
+ fal/cli/run.py,sha256=gZ4mtXEQ2lv2x_4RHigVQdsHpbZBSS8sFF3Kxq5dF2w,1578
47
+ fal/cli/runners.py,sha256=HnFEPT0pXgQkXQr7b9ria2VwfeUx1yPdOKJYZE0r1MM,21741
48
+ fal/cli/secrets.py,sha256=VSC1ybGz6wAQjYz7rbxCG7EXgCpLvNXuN8c8uH2w7F8,2931
48
49
  fal/cli/teams.py,sha256=_JcNcf659ZoLBFOxKnVP5A6Pyk1jY1vh4_xzMweYIDo,1285
49
50
  fal/console/__init__.py,sha256=lGPUuTqIM9IKTa1cyyA-MA2iZJKVHp2YydsITZVlb6g,148
50
51
  fal/console/icons.py,sha256=De9MfFaSkO2Lqfne13n3PrYfTXJVIzYZVqYn5BWsdrA,108
@@ -60,7 +61,8 @@ fal/logging/__init__.py,sha256=SRuG6TpTmxFmPtAKH0ZBqhpvahfBccFbaKNvKRZPPd0,1320
60
61
  fal/logging/isolate.py,sha256=jIryi46ZVlJ1mfan4HLNQQ3jwMi8z-WwfqqLlttQVkc,2449
61
62
  fal/logging/style.py,sha256=ckIgHzvF4DShM5kQh8F133X53z_vF46snuDHVmo_h9g,386
62
63
  fal/logging/trace.py,sha256=OhzB6d4rQZimBc18WFLqH_9BGfqFFumKKTAGSsmWRMg,1904
63
- fal/toolkit/__init__.py,sha256=GR5KxAsNODlhs-DTarJcb5raujActubn0afR7FPueWs,886
64
+ fal/toolkit/__init__.py,sha256=o9Nk3D6L15dzDWewcXHOj4KRB9wfsuLA3lXRaiJnhrE,1136
65
+ fal/toolkit/compilation.py,sha256=4Srs7GHuA0V3L4a29UVL7eBM1NBZLFuyhY8hLZOXR3U,7522
64
66
  fal/toolkit/exceptions.py,sha256=8-EMuqDXEofPu-eVoWowc7WEM-ifusithyv6tnsm2MM,301
65
67
  fal/toolkit/kv.py,sha256=5kMk-I5PMRORK4TYc0jqqowjqKkbk7zUIgz9rAIztxE,2364
66
68
  fal/toolkit/optimize.py,sha256=p75sovF0SmRP6zxzpIaaOmqlxvXB_xEz3XPNf59EF7w,1339
@@ -68,7 +70,7 @@ fal/toolkit/types.py,sha256=kkbOsDKj1qPGb1UARTBp7yuJ5JUuyy7XQurYUBCdti8,4064
68
70
  fal/toolkit/audio/__init__.py,sha256=sqNVfrKbppWlIGLoFTaaNTxLpVXsFHxOSHLA5VG547A,35
69
71
  fal/toolkit/audio/audio.py,sha256=gt458h989iQ-EhQSH-mCuJuPBY4RneLJE05f_QWU1E0,572
70
72
  fal/toolkit/file/__init__.py,sha256=FbNl6wD-P0aSSTUwzHt4HujBXrbC3ABmaigPQA4hRfg,70
71
- fal/toolkit/file/file.py,sha256=_KCKmtmBkBIKD_gFOZALV10dCtOFZTC9MQw2qmdeevw,11013
73
+ fal/toolkit/file/file.py,sha256=9ucrzi1GiKKauSljDoYibJl0eQegpk8D47XGFDLC_0w,11130
72
74
  fal/toolkit/file/types.py,sha256=MMAH_AyLOhowQPesOv1V25wB4qgbJ3vYNlnTPbdSv1M,2304
73
75
  fal/toolkit/file/providers/fal.py,sha256=P9hm11uKVe6ilmL7CjFztBHswZEHOm4k-K4B36UZe6M,47543
74
76
  fal/toolkit/file/providers/gcp.py,sha256=DKeZpm1MjwbvEsYvkdXUtuLIJDr_UNbqXj_Mfv3NTeo,2437
@@ -152,8 +154,8 @@ openapi_fal_rest/models/workflow_node_type.py,sha256=-FzyeY2bxcNmizKbJI8joG7byRi
152
154
  openapi_fal_rest/models/workflow_schema.py,sha256=4K5gsv9u9pxx2ItkffoyHeNjBBYf6ur5bN4m_zePZNY,2019
153
155
  openapi_fal_rest/models/workflow_schema_input.py,sha256=2OkOXWHTNsCXHWS6EGDFzcJKkW5FIap-2gfO233EvZQ,1191
154
156
  openapi_fal_rest/models/workflow_schema_output.py,sha256=EblwSPAGfWfYVWw_WSSaBzQVju296is9o28rMBAd0mc,1196
155
- fal-1.50.1.dist-info/METADATA,sha256=xpBAJQgk3Bc9GMkOGbqAEa7KCmh0p94ABjpr_MS_DwU,4250
156
- fal-1.50.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
157
- fal-1.50.1.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
158
- fal-1.50.1.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
159
- fal-1.50.1.dist-info/RECORD,,
157
+ fal-1.57.2.dist-info/METADATA,sha256=PAFHX10gB7KhkGT6puVZfCDuZ4milOfIdh2VzQUBBAE,4236
158
+ fal-1.57.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
159
+ fal-1.57.2.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
160
+ fal-1.57.2.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
161
+ fal-1.57.2.dist-info/RECORD,,
fal/rest_client.py DELETED
@@ -1,25 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from openapi_fal_rest.client import Client
4
-
5
- import fal.flags as flags
6
- from fal.sdk import get_default_credentials
7
-
8
-
9
- class CredentialsClient(Client):
10
- def get_headers(self) -> dict[str, str]:
11
- creds = get_default_credentials()
12
- return {
13
- **creds.to_headers(),
14
- **self.headers,
15
- }
16
-
17
-
18
- # TODO: accept more auth methods
19
- REST_CLIENT = CredentialsClient(
20
- flags.REST_URL,
21
- timeout=30,
22
- verify_ssl=not flags.TEST_MODE,
23
- raise_on_unexpected_status=False,
24
- follow_redirects=True,
25
- )
File without changes