fal 1.2.1__py3-none-any.whl → 1.2.3__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
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.2.1'
16
- __version_tuple__ = version_tuple = (1, 2, 1)
15
+ __version__ = version = '1.2.3'
16
+ __version_tuple__ = version_tuple = (1, 2, 3)
fal/api.py CHANGED
@@ -1048,7 +1048,9 @@ class BaseServable:
1048
1048
  from uvicorn import Config
1049
1049
 
1050
1050
  app = self._build_app()
1051
- server = Server(config=Config(app, host="0.0.0.0", port=8080))
1051
+ server = Server(
1052
+ config=Config(app, host="0.0.0.0", port=8080, timeout_keep_alive=300)
1053
+ )
1052
1054
  metrics_app = FastAPI()
1053
1055
  metrics_app.add_route("/metrics", handle_metrics)
1054
1056
  metrics_server = Server(config=Config(metrics_app, host="0.0.0.0", port=9090))
fal/app.py CHANGED
@@ -7,7 +7,7 @@ import re
7
7
  import time
8
8
  import typing
9
9
  from contextlib import asynccontextmanager, contextmanager
10
- from typing import Any, Callable, ClassVar, TypeVar
10
+ from typing import Any, Callable, ClassVar, Literal, TypeVar
11
11
 
12
12
  import httpx
13
13
  from fastapi import FastAPI
@@ -152,12 +152,13 @@ class App(fal.api.BaseServable):
152
152
  "keep_alive": 60,
153
153
  }
154
154
  app_name: ClassVar[str]
155
+ app_auth: ClassVar[Literal["private", "public", "shared"]] = "private"
155
156
 
156
157
  def __init_subclass__(cls, **kwargs):
157
158
  app_name = kwargs.pop("name", None) or _to_fal_app_name(cls.__name__)
158
159
  parent_settings = getattr(cls, "host_kwargs", {})
159
160
  cls.host_kwargs = {**parent_settings, **kwargs}
160
- cls.app_name = app_name
161
+ cls.app_name = getattr(cls, "app_name", app_name)
161
162
 
162
163
  if cls.__init__ is not App.__init__:
163
164
  raise ValueError(
fal/cli/apps.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from typing import TYPE_CHECKING
2
4
 
3
5
  from .parser import FalClientParser
@@ -6,7 +8,7 @@ if TYPE_CHECKING:
6
8
  from fal.sdk import AliasInfo, ApplicationInfo
7
9
 
8
10
 
9
- def _apps_table(apps: list["AliasInfo"]):
11
+ def _apps_table(apps: list[AliasInfo]):
10
12
  from rich.table import Table
11
13
 
12
14
  table = Table()
@@ -56,7 +58,7 @@ def _add_list_parser(subparsers, parents):
56
58
  parser.set_defaults(func=_list)
57
59
 
58
60
 
59
- def _app_rev_table(revs: list["ApplicationInfo"]):
61
+ def _app_rev_table(revs: list[ApplicationInfo]):
60
62
  from rich.table import Table
61
63
 
62
64
  table = Table()
fal/cli/create.py ADDED
@@ -0,0 +1,26 @@
1
+ PROJECT_TYPES = ["app"]
2
+
3
+
4
+ def _create_project(project_type: str):
5
+ from cookiecutter.main import cookiecutter
6
+
7
+ cookiecutter("https://github.com/fal-ai/cookiecutter-fal.git")
8
+
9
+
10
+ def add_parser(main_subparsers, parents):
11
+ apps_help = "Create fal applications."
12
+ parser = main_subparsers.add_parser(
13
+ "create",
14
+ description=apps_help,
15
+ help=apps_help,
16
+ parents=parents,
17
+ )
18
+
19
+ parser.add_argument(
20
+ metavar="project_type",
21
+ choices=PROJECT_TYPES,
22
+ help="Type of project to create.",
23
+ dest="project_type",
24
+ )
25
+
26
+ parser.set_defaults(func=_create_project)
fal/cli/deploy.py CHANGED
@@ -81,17 +81,18 @@ def _deploy(args):
81
81
 
82
82
  user = _get_user()
83
83
  host = FalServerlessHost(args.host)
84
- isolated_function, app_name = load_function_from(
84
+ isolated_function, app_name, app_auth = load_function_from(
85
85
  host,
86
86
  file_path,
87
87
  func_name,
88
88
  )
89
89
  app_name = args.app_name or app_name
90
+ app_auth = args.auth or app_auth or "private"
90
91
  app_id = host.register(
91
92
  func=isolated_function.func,
92
93
  options=isolated_function.options,
93
94
  application_name=app_name,
94
- application_auth_mode=args.auth,
95
+ application_auth_mode=app_auth,
95
96
  metadata=isolated_function.options.host.get("metadata", {}),
96
97
  )
97
98
 
@@ -151,7 +152,6 @@ def add_parser(main_subparsers, parents):
151
152
  parser.add_argument(
152
153
  "--auth",
153
154
  type=valid_auth_option,
154
- default="private",
155
155
  help="Application authentication mode (private, public).",
156
156
  )
157
157
  parser.set_defaults(func=_deploy)
fal/cli/main.py CHANGED
@@ -6,7 +6,7 @@ from fal import __version__
6
6
  from fal.console import console
7
7
  from fal.console.icons import CROSS_ICON
8
8
 
9
- from . import apps, auth, deploy, doctor, keys, run, secrets
9
+ from . import apps, auth, create, deploy, doctor, keys, run, secrets
10
10
  from .debug import debugtools, get_debug_parser
11
11
  from .parser import FalParser, FalParserExit
12
12
 
@@ -31,7 +31,7 @@ def _get_main_parser() -> argparse.ArgumentParser:
31
31
  required=True,
32
32
  )
33
33
 
34
- for cmd in [auth, apps, deploy, run, keys, secrets, doctor]:
34
+ for cmd in [auth, apps, deploy, run, keys, secrets, doctor, create]:
35
35
  cmd.add_parser(subparsers, parents)
36
36
 
37
37
  return parser
fal/cli/run.py CHANGED
@@ -6,7 +6,7 @@ def _run(args):
6
6
  from fal.utils import load_function_from
7
7
 
8
8
  host = FalServerlessHost(args.host)
9
- isolated_function, _ = load_function_from(host, *args.func_ref)
9
+ isolated_function, _, _ = load_function_from(host, *args.func_ref)
10
10
  # let our exc handlers handle UserFunctionException
11
11
  isolated_function.reraise = False
12
12
  isolated_function()
@@ -1,3 +1,74 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from functools import lru_cache
4
+ from typing import TYPE_CHECKING
5
+ from urllib.request import Request, urlopen
6
+
3
7
  from .image import * # noqa: F403
8
+
9
+ if TYPE_CHECKING:
10
+ from PIL.Image import Image as PILImage
11
+
12
+
13
+ def filter_by(
14
+ has_nsfw_concepts: list[bool],
15
+ images: list[PILImage],
16
+ ) -> list[PILImage]:
17
+ from PIL import Image as PILImage
18
+
19
+ return [
20
+ (
21
+ PILImage.new("RGB", (image.width, image.height), (0, 0, 0))
22
+ if has_nsfw
23
+ else image
24
+ )
25
+ for image, has_nsfw in zip(images, has_nsfw_concepts)
26
+ ]
27
+
28
+
29
+ def preprocess_image(image_pil, convert_to_rgb=True, fix_orientation=True):
30
+ from PIL import ImageOps, ImageSequence
31
+
32
+ # For MPO (multi picture object) format images, we only need the first image
33
+ images = []
34
+ for image in ImageSequence.Iterator(image_pil):
35
+ img = image
36
+
37
+ if convert_to_rgb:
38
+ img = img.convert("RGB")
39
+
40
+ if fix_orientation:
41
+ img = ImageOps.exif_transpose(img)
42
+
43
+ images.append(img)
44
+
45
+ break
46
+
47
+ return images[0]
48
+
49
+
50
+ @lru_cache(maxsize=64)
51
+ def read_image_from_url(
52
+ url: str, convert_to_rgb: bool = True, fix_orientation: bool = True
53
+ ):
54
+ from fastapi import HTTPException
55
+ from PIL import Image
56
+
57
+ TEMP_HEADERS = {
58
+ "User-Agent": (
59
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) "
60
+ "Gecko/20100101 Firefox/21.0"
61
+ ),
62
+ }
63
+
64
+ try:
65
+ request = Request(url, headers=TEMP_HEADERS)
66
+ response = urlopen(request)
67
+ image_pil = Image.open(response)
68
+ except Exception:
69
+ import traceback
70
+
71
+ traceback.print_exc()
72
+ raise HTTPException(422, f"Could not load image from url: {url}")
73
+
74
+ return preprocess_image(image_pil, convert_to_rgb, fix_orientation)
@@ -0,0 +1,11 @@
1
+ from .inference import (
2
+ NSFWImageDetectionInput,
3
+ NSFWImageDetectionOutput,
4
+ run_nsfw_estimation,
5
+ )
6
+
7
+ __all__ = [
8
+ "NSFWImageDetectionInput",
9
+ "NSFWImageDetectionOutput",
10
+ "run_nsfw_estimation",
11
+ ]
@@ -0,0 +1,9 @@
1
+ from pathlib import Path
2
+
3
+ CURR_DIR = Path(__file__).resolve().parent
4
+
5
+
6
+ def get_requirements():
7
+ with open(CURR_DIR / "requirements.txt") as fp:
8
+ requirements = fp.read().split()
9
+ return requirements
@@ -0,0 +1,77 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ import fal
4
+ from fal.toolkit.image import read_image_from_url
5
+
6
+ from .env import get_requirements
7
+ from .model import get_model
8
+
9
+
10
+ class NSFWImageDetectionInput(BaseModel):
11
+ image_url: str = Field(
12
+ description="Input image url.",
13
+ examples=[
14
+ "https://storage.googleapis.com/falserverless/model_tests/remove_background/elephant.jpg",
15
+ ],
16
+ )
17
+
18
+
19
+ class NSFWImageDetectionOutput(BaseModel):
20
+ nsfw_probability: float = Field(
21
+ description="The probability of the image being NSFW.",
22
+ )
23
+
24
+
25
+ def check_nsfw_content(pil_image: object):
26
+ import torch
27
+
28
+ model, processor = get_model()
29
+
30
+ with torch.no_grad():
31
+ inputs = processor(images=pil_image, return_tensors="pt")
32
+ outputs = model(**inputs)
33
+ logits = outputs.logits.squeeze() # Remove batch dimension to simplify indexing
34
+
35
+ # Apply softmax to convert logits to probabilities
36
+ probabilities = torch.softmax(logits, dim=0)
37
+
38
+ nsfw_class_index = model.config.label2id.get(
39
+ "nsfw", None
40
+ ) # Replace "NSFW" with the exact class name if different
41
+
42
+ # Validate that NSFW class index is found
43
+ if nsfw_class_index is not None:
44
+ nsfw_probability = probabilities[int(nsfw_class_index)].item()
45
+ return nsfw_probability
46
+ else:
47
+ raise ValueError("NSFW class not found in model output.")
48
+
49
+
50
+ def run_nsfw_estimation(
51
+ input: NSFWImageDetectionInput,
52
+ ) -> NSFWImageDetectionOutput:
53
+ img = read_image_from_url(input.image_url)
54
+ nsfw_probability = check_nsfw_content(img)
55
+
56
+ return NSFWImageDetectionOutput(nsfw_probability=nsfw_probability)
57
+
58
+
59
+ @fal.function(
60
+ requirements=get_requirements(),
61
+ machine_type="GPU-A6000",
62
+ serve=True,
63
+ )
64
+ def run_nsfw_estimation_on_fal(
65
+ input: NSFWImageDetectionInput,
66
+ ) -> NSFWImageDetectionOutput:
67
+ return run_nsfw_estimation(input)
68
+
69
+
70
+ if __name__ == "__main__":
71
+ local = run_nsfw_estimation_on_fal.on(serve=False)
72
+ result = local(
73
+ NSFWImageDetectionInput(
74
+ image_url="https://storage.googleapis.com/falserverless/model_tests/remove_background/elephant.jpg",
75
+ )
76
+ )
77
+ print(result)
@@ -0,0 +1,18 @@
1
+ import fal
2
+
3
+
4
+ @fal.cached
5
+ def get_model():
6
+ import os
7
+
8
+ from transformers import AutoModelForImageClassification, ViTImageProcessor
9
+
10
+ os.environ["TRANSFORMERS_CACHE"] = "/data/models"
11
+ os.environ["HF_HOME"] = "/data/models"
12
+
13
+ model = AutoModelForImageClassification.from_pretrained(
14
+ "Falconsai/nsfw_image_detection"
15
+ )
16
+ processor = ViTImageProcessor.from_pretrained("Falconsai/nsfw_image_detection")
17
+
18
+ return model, processor
@@ -0,0 +1,4 @@
1
+ accelerate
2
+ Pillow
3
+ torch
4
+ transformers
@@ -0,0 +1,107 @@
1
+ from typing import Any
2
+
3
+ import fal
4
+
5
+ from . import filter_by
6
+ from .nsfw_filter.model import get_model
7
+
8
+
9
+ @fal.cached
10
+ def load_safety_checker():
11
+ import torch
12
+ from diffusers.pipelines.stable_diffusion.safety_checker import (
13
+ StableDiffusionSafetyChecker,
14
+ )
15
+ from transformers import AutoFeatureExtractor
16
+
17
+ feature_extractor = AutoFeatureExtractor.from_pretrained(
18
+ "CompVis/stable-diffusion-safety-checker",
19
+ torch_dtype="float16",
20
+ )
21
+ safety_checker = StableDiffusionSafetyChecker.from_pretrained(
22
+ "CompVis/stable-diffusion-safety-checker",
23
+ torch_dtype=torch.float16,
24
+ ).to("cuda")
25
+
26
+ return feature_extractor, safety_checker
27
+
28
+
29
+ def run_safety_checker(
30
+ pil_images: list[object],
31
+ ) -> list[bool]:
32
+ import numpy as np
33
+ import torch
34
+
35
+ feature_extractor, safety_checker = load_safety_checker()
36
+
37
+ safety_checker_input = feature_extractor(pil_images, return_tensors="pt").to("cuda")
38
+
39
+ np_image = [np.array(val) for val in pil_images]
40
+
41
+ _, has_nsfw_concept = safety_checker(
42
+ images=np_image,
43
+ clip_input=safety_checker_input.pixel_values.to(torch.float16),
44
+ )
45
+
46
+ return has_nsfw_concept
47
+
48
+
49
+ def run_safety_checker_v2(pil_images: list, nsfw_threshold: float = 0.5) -> list[bool]:
50
+ import torch
51
+
52
+ model, processor = get_model()
53
+
54
+ has_nsfw_concept = []
55
+
56
+ with torch.no_grad():
57
+ for pil_image in pil_images:
58
+ inputs = processor(
59
+ images=pil_image.convert("RGB"),
60
+ return_tensors="pt",
61
+ )
62
+ outputs = model(**inputs)
63
+ logits = (
64
+ outputs.logits.squeeze()
65
+ ) # Remove batch dimension to simplify indexing
66
+
67
+ # Apply softmax to convert logits to probabilities
68
+ probabilities = torch.softmax(logits, dim=0)
69
+
70
+ nsfw_class_index = model.config.label2id.get(
71
+ "nsfw", None
72
+ ) # Replace "NSFW" with the exact class name if different
73
+
74
+ # Validate that NSFW class index is found
75
+ if nsfw_class_index is not None:
76
+ nsfw_probability = probabilities[int(nsfw_class_index)].item()
77
+ print("NSFW probability:", nsfw_probability)
78
+ has_nsfw_concept.append(nsfw_probability > nsfw_threshold)
79
+ else:
80
+ raise ValueError("NSFW class not found in model output.")
81
+
82
+ return has_nsfw_concept
83
+
84
+
85
+ def postprocess_images(
86
+ pil_images: list[object],
87
+ enable_safety_checker: bool = True,
88
+ safety_checker_version: int = 2,
89
+ ) -> dict[str, Any]:
90
+ outputs: dict[str, list[Any]] = {
91
+ "images": pil_images,
92
+ }
93
+
94
+ if enable_safety_checker:
95
+ safety_checker_fn = (
96
+ run_safety_checker_v2 if safety_checker_version == 2 else run_safety_checker
97
+ )
98
+ outputs["has_nsfw_concepts"] = safety_checker_fn(pil_images) # type: ignore
99
+ else:
100
+ outputs["has_nsfw_concepts"] = [False] * len(pil_images)
101
+
102
+ outputs["images"] = filter_by(
103
+ outputs["has_nsfw_concepts"],
104
+ outputs["images"],
105
+ )
106
+
107
+ return outputs
fal/utils.py CHANGED
@@ -10,13 +10,13 @@ def load_function_from(
10
10
  host: FalServerlessHost,
11
11
  file_path: str,
12
12
  function_name: str | None = None,
13
- ) -> tuple[IsolatedFunction, str | None]:
13
+ ) -> tuple[IsolatedFunction, str | None, str | None]:
14
14
  import runpy
15
15
 
16
16
  module = runpy.run_path(file_path)
17
17
  if function_name is None:
18
18
  fal_objects = {
19
- obj.app_name: obj_name
19
+ obj_name: obj
20
20
  for obj_name, obj in module.items()
21
21
  if isinstance(obj, type)
22
22
  and issubclass(obj, fal.App)
@@ -30,9 +30,12 @@ def load_function_from(
30
30
  "Please specify the name of the app."
31
31
  )
32
32
 
33
- [(app_name, function_name)] = fal_objects.items()
33
+ [(function_name, obj)] = fal_objects.items()
34
+ app_name = obj.app_name
35
+ app_auth = obj.app_auth
34
36
  else:
35
37
  app_name = None
38
+ app_auth = None
36
39
 
37
40
  if function_name not in module:
38
41
  raise FalServerlessError(f"Function '{function_name}' not found in module")
@@ -44,10 +47,11 @@ def load_function_from(
44
47
  target = module[function_name]
45
48
  if isinstance(target, type) and issubclass(target, App):
46
49
  app_name = target.app_name
50
+ app_auth = target.app_auth
47
51
  target = wrap_app(target, host=host)
48
52
 
49
53
  if not isinstance(target, IsolatedFunction):
50
54
  raise FalServerlessError(
51
55
  f"Function '{function_name}' is not a fal.function or a fal.App"
52
56
  )
53
- return target, app_name
57
+ return target, app_name, app_auth
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fal
3
- Version: 1.2.1
3
+ Version: 1.2.3
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
@@ -34,6 +34,7 @@ Requires-Dist: websockets <13,>=12.0
34
34
  Requires-Dist: pillow <11,>=10.2.0
35
35
  Requires-Dist: pyjwt[crypto] <3,>=2.8.0
36
36
  Requires-Dist: uvicorn <1,>=0.29.0
37
+ Requires-Dist: cookiecutter
37
38
  Requires-Dist: importlib-metadata >=4.4 ; python_version < "3.10"
38
39
  Provides-Extra: dev
39
40
  Requires-Dist: fal[test] ; extra == 'dev'
@@ -1,10 +1,10 @@
1
1
  fal/__init__.py,sha256=wXs1G0gSc7ZK60-bHe-B2m0l_sA6TrFk4BxY0tMoLe8,784
2
2
  fal/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
3
- fal/_fal_version.py,sha256=2U0Gn26fYI3Vgj5hgkLM8I3wI6YEVdffJGllaVW-sSc,411
3
+ fal/_fal_version.py,sha256=VUBVzEBuW57s195P22MF-rLGH7GWx3G_z5nV-l_8MBE,411
4
4
  fal/_serialization.py,sha256=rD2YiSa8iuzCaZohZwN_MPEB-PpSKbWRDeaIDpTEjyY,7653
5
5
  fal/_version.py,sha256=EBGqrknaf1WygENX-H4fBefLvHryvJBBGtVJetaB0NY,266
6
- fal/api.py,sha256=x60GlBWynDd1yhHsVWeqf07WVTzgbwNC6cqCjhlTiFQ,40556
7
- fal/app.py,sha256=duOf_YKE8o30hmhNtF9zvkT8wlKYXW7hdQLJtPrXHik,15793
6
+ fal/api.py,sha256=LAPl5Hf6ZWzEjv4lFUtsisWgrnXH_qNUHdJrEHT_A5Y,40602
7
+ fal/app.py,sha256=9HJGu_64ArtW8W91BC0U4Etr2gA31LXaHgR6HzoOops,15903
8
8
  fal/apps.py,sha256=FrKmaAUo8U9vE_fcva0GQvk4sCrzaTEr62lGtu3Ld5M,6825
9
9
  fal/container.py,sha256=V7riyyq8AZGwEX9QaqRQDZyDN_bUKeRKV1OOZArXjL0,622
10
10
  fal/flags.py,sha256=oWN_eidSUOcE9wdPK_77si3A1fpgOC0UEERPsvNLIMc,842
@@ -12,21 +12,22 @@ fal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  fal/rest_client.py,sha256=kGBGmuyHfX1lR910EoKCYPjsyU8MdXawT_cW2q8Sajc,568
13
13
  fal/sdk.py,sha256=wA58DYnSK1vdsBi8Or9Z8kvMMEyBNfeZYk_xulSfTWE,20078
14
14
  fal/sync.py,sha256=ZuIJA2-hTPNANG9B_NNJZUsO68EIdTH0dc9MzeVE2VU,4340
15
- fal/utils.py,sha256=pstF7-13xjB1gKOtzz4tIXc8b8nMSDd9HO79WCiVrt4,1753
15
+ fal/utils.py,sha256=4-V6iGSRd3kG_-UP6OdZp_-EhAkl3zectFlFKkCsS0Q,1884
16
16
  fal/workflows.py,sha256=jx3tGy2R7cN6lLvOzT6lhhlcjmiq64iZls2smVrmQj0,14657
17
17
  fal/auth/__init__.py,sha256=r8iA2-5ih7-Fik3gEC4HEWNFbGoxpYnXpZu1icPIoS0,3561
18
18
  fal/auth/auth0.py,sha256=rSG1mgH-QGyKfzd7XyAaj1AYsWt-ho8Y_LZ-FUVWzh4,5421
19
19
  fal/auth/local.py,sha256=sndkM6vKpeVny6NHTacVlTbiIFqaksOmw0Viqs_RN1U,1790
20
20
  fal/cli/__init__.py,sha256=padK4o0BFqq61kxAA1qQ0jYr2SuhA2mf90B3AaRkmJA,37
21
- fal/cli/apps.py,sha256=GlQSEE68HYfnhK90WIiOx611tcVkkbc9bu_10FMaFLY,8143
21
+ fal/cli/apps.py,sha256=-DDp-Gvxz5kHho5YjAhbri8vOny_9cftAI_wP2KR5nU,8175
22
22
  fal/cli/auth.py,sha256=--MhfHGwxmtHbRkGioyn1prKn_U-pBzbz0G_QeZou-U,1352
23
+ fal/cli/create.py,sha256=a8WDq-nJLFTeoIXqpb5cr7GR7YR9ZZrQCawNm34KXXE,627
23
24
  fal/cli/debug.py,sha256=u_urnyFzSlNnrq93zz_GXE9FX4VyVxDoamJJyrZpFI0,1312
24
- fal/cli/deploy.py,sha256=jA-C4Pvzie70MVDnaFKc6ByfBrNFeaMROAKX98RD8tQ,4785
25
+ fal/cli/deploy.py,sha256=1e4OERVGtfwgM0VEFjlCLpNyuOl1BiLI-dx8u-71PVs,4817
25
26
  fal/cli/doctor.py,sha256=U4ne9LX5gQwNblsYQ27XdO8AYDgbYjTO39EtxhwexRM,983
26
27
  fal/cli/keys.py,sha256=trDpA3LJu9S27qE_K8Hr6fKLK4vwVzbxUHq8TFrV4pw,3157
27
- fal/cli/main.py,sha256=MxETDhqIT37quMbmofSMxBcAFOhnEHjpQ_pYEtOhApM,1993
28
+ fal/cli/main.py,sha256=_Wh_DQc02qwh-ZN7v41lZm0lDR1WseViXVOcqUlyWLg,2009
28
29
  fal/cli/parser.py,sha256=r1hd5e8Jq6yzDZw8-S0On1EjJbjRtHMuVuHC6MlvUj4,2835
29
- fal/cli/run.py,sha256=enhnpqo07PzGK2uh8M522zenNWxrBFOqb3oMZ8XiWdg,870
30
+ fal/cli/run.py,sha256=8wHNDruIr8i21JwbfFzS389C-y0jktM5zN5iDnJHsvA,873
30
31
  fal/cli/secrets.py,sha256=740msFm7d41HruudlcfqUXlFl53N-WmChsQP9B9M9Po,2572
31
32
  fal/console/__init__.py,sha256=ernZ4bzvvliQh5SmrEqQ7lA5eVcbw6Ra2jalKtA7dxg,132
32
33
  fal/console/icons.py,sha256=De9MfFaSkO2Lqfne13n3PrYfTXJVIzYZVqYn5BWsdrA,108
@@ -48,8 +49,14 @@ fal/toolkit/file/types.py,sha256=bJCeV5NPcpJYJoglailiRgFsuNAfcextYA8Et5-XUag,106
48
49
  fal/toolkit/file/providers/fal.py,sha256=65-BkK9jhGBwYI_OjhHJsL2DthyKxBBRrqXPI_ZN4-k,4115
49
50
  fal/toolkit/file/providers/gcp.py,sha256=pUVH2qNcnO_VrDQQU8MmfYOQZMGaKQIqE4yGnYdQhAc,2003
50
51
  fal/toolkit/file/providers/r2.py,sha256=WxmOHF5WxHt6tKMcFjWj7ZWO8a1EXysO9lfYv_tB3MI,2627
51
- fal/toolkit/image/__init__.py,sha256=qNLyXsBWysionUjbeWbohLqWlw3G_UpzunamkZd_JLQ,71
52
+ fal/toolkit/image/__init__.py,sha256=aLcU8HzD7HyOxx-C-Bbx9kYCMHdBhy9tR98FSVJ6gSA,1830
52
53
  fal/toolkit/image/image.py,sha256=UDIHgkxae8LzmCvWBM9GayMnK8c0JMMfsrVlLnW5rto,4234
54
+ fal/toolkit/image/safety_checker.py,sha256=S7ow-HuoVxC6ixHWWcBrAUm2dIlgq3sTAIull6xIbAg,3105
55
+ fal/toolkit/image/nsfw_filter/__init__.py,sha256=0d9D51EhcnJg8cZLYJjgvQJDZT74CfQu6mpvinRYRpA,216
56
+ fal/toolkit/image/nsfw_filter/env.py,sha256=iAP2Q3vzIl--DD8nr8o3o0goAwhExN2v0feYE0nIQjs,212
57
+ fal/toolkit/image/nsfw_filter/inference.py,sha256=BhIPF_zxRLetThQYxDDF0sdx9VRwvu74M5ye6Povi40,2167
58
+ fal/toolkit/image/nsfw_filter/model.py,sha256=63mu8D15z_IosoRUagRLGHy6VbLqFmrG-yZqnu2vVm4,457
59
+ fal/toolkit/image/nsfw_filter/requirements.txt,sha256=3Pmrd0Ny6QAeBqUNHCgffRyfaCARAPJcfSCX5cRYpbM,37
53
60
  fal/toolkit/utils/__init__.py,sha256=CrmM9DyCz5-SmcTzRSm5RaLgxy3kf0ZsSEN9uhnX2Xo,97
54
61
  fal/toolkit/utils/download_utils.py,sha256=9WMpn0mFIhkFelQpPj5KG-pC7RMyyOzGHbNRDSyz07o,17664
55
62
  openapi_fal_rest/__init__.py,sha256=ziculmF_i6trw63LzZGFX-6W3Lwq9mCR8_UpkpvpaHI,152
@@ -115,8 +122,8 @@ openapi_fal_rest/models/workflow_node_type.py,sha256=-FzyeY2bxcNmizKbJI8joG7byRi
115
122
  openapi_fal_rest/models/workflow_schema.py,sha256=4K5gsv9u9pxx2ItkffoyHeNjBBYf6ur5bN4m_zePZNY,2019
116
123
  openapi_fal_rest/models/workflow_schema_input.py,sha256=2OkOXWHTNsCXHWS6EGDFzcJKkW5FIap-2gfO233EvZQ,1191
117
124
  openapi_fal_rest/models/workflow_schema_output.py,sha256=EblwSPAGfWfYVWw_WSSaBzQVju296is9o28rMBAd0mc,1196
118
- fal-1.2.1.dist-info/METADATA,sha256=1ViXkmFXOXQqD52q0HPxP-BuzHL5RzQ6x6MC__GOXn4,3777
119
- fal-1.2.1.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
120
- fal-1.2.1.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
121
- fal-1.2.1.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
122
- fal-1.2.1.dist-info/RECORD,,
125
+ fal-1.2.3.dist-info/METADATA,sha256=tUsGGaLLvqfYpEePodE0fuc5nm_XSoYE7AWTrci2nEY,3805
126
+ fal-1.2.3.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
127
+ fal-1.2.3.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
128
+ fal-1.2.3.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
129
+ fal-1.2.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.3.0)
2
+ Generator: setuptools (72.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5