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.
fal/_fal_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.50.1'
32
- __version_tuple__ = version_tuple = (1, 50, 1)
31
+ __version__ = version = '1.57.2'
32
+ __version_tuple__ = version_tuple = (1, 57, 2)
33
33
 
34
34
  __commit_id__ = commit_id = None
fal/api/api.py CHANGED
@@ -68,6 +68,7 @@ from fal.sdk import (
68
68
  File,
69
69
  HostedRunState,
70
70
  MachineRequirements,
71
+ RegisterApplicationResult,
71
72
  get_agent_credentials,
72
73
  get_default_credentials,
73
74
  )
@@ -84,6 +85,7 @@ _UNSET = object()
84
85
  SERVE_REQUIREMENTS = [
85
86
  f"fastapi=={fastapi_version}",
86
87
  f"pydantic=={pydantic_version}",
88
+ f"tblib=={tblib.__version__}",
87
89
  "uvicorn",
88
90
  "starlette_exporter",
89
91
  # workaround for prometheus_client 0.23.0
@@ -426,12 +428,14 @@ class FalServerlessHost(Host):
426
428
  {
427
429
  "machine_type",
428
430
  "machine_types",
431
+ "regions",
429
432
  "num_gpus",
430
433
  "keep_alive",
431
434
  "max_concurrency",
432
435
  "min_concurrency",
433
436
  "concurrency_buffer",
434
437
  "concurrency_buffer_perc",
438
+ "scaling_delay",
435
439
  "max_multiplexing",
436
440
  "setup_function",
437
441
  "metadata",
@@ -444,6 +448,7 @@ class FalServerlessHost(Host):
444
448
  "app_files",
445
449
  "app_files_ignore",
446
450
  "app_files_context_dir",
451
+ "health_check_path",
447
452
  }
448
453
  )
449
454
 
@@ -518,7 +523,7 @@ class FalServerlessHost(Host):
518
523
  metadata: Optional[dict[str, Any]] = None,
519
524
  deployment_strategy: DeploymentStrategyLiteral,
520
525
  scale: bool = True,
521
- ) -> Optional[str]:
526
+ ) -> Optional[RegisterApplicationResult]:
522
527
  from isolate.backends.common import active_python
523
528
 
524
529
  environment_options = options.environment.copy()
@@ -536,10 +541,12 @@ class FalServerlessHost(Host):
536
541
  min_concurrency = options.host.get("min_concurrency")
537
542
  concurrency_buffer = options.host.get("concurrency_buffer")
538
543
  concurrency_buffer_perc = options.host.get("concurrency_buffer_perc")
544
+ scaling_delay = options.host.get("scaling_delay")
539
545
  max_multiplexing = options.host.get("max_multiplexing")
540
546
  exposed_port = options.get_exposed_port()
541
547
  request_timeout = options.host.get("request_timeout")
542
548
  startup_timeout = options.host.get("startup_timeout")
549
+ regions = options.host.get("regions")
543
550
  machine_requirements = MachineRequirements(
544
551
  machine_types=machine_type, # type: ignore
545
552
  num_gpus=options.host.get("num_gpus"),
@@ -553,10 +560,14 @@ class FalServerlessHost(Host):
553
560
  min_concurrency=min_concurrency,
554
561
  concurrency_buffer=concurrency_buffer,
555
562
  concurrency_buffer_perc=concurrency_buffer_perc,
563
+ scaling_delay=scaling_delay,
556
564
  request_timeout=request_timeout,
557
565
  startup_timeout=startup_timeout,
566
+ valid_regions=regions,
558
567
  )
559
568
 
569
+ health_check_path = options.host.get("health_check_path")
570
+
560
571
  app_files = self._app_files_sync(options)
561
572
 
562
573
  partial_func = _prepare_partial_func(func)
@@ -580,6 +591,7 @@ class FalServerlessHost(Host):
580
591
  metadata=metadata,
581
592
  deployment_strategy=deployment_strategy,
582
593
  scale=scale,
594
+ health_check_path=health_check_path,
583
595
  # By default, logs are public
584
596
  private_logs=options.host.get("private_logs", False),
585
597
  files=app_files,
@@ -588,7 +600,7 @@ class FalServerlessHost(Host):
588
600
  self._log_printer.print(log)
589
601
 
590
602
  if partial_result.result:
591
- return partial_result.result.application_id
603
+ return partial_result
592
604
 
593
605
  return None
594
606
 
@@ -615,6 +627,7 @@ class FalServerlessHost(Host):
615
627
  min_concurrency = options.host.get("min_concurrency")
616
628
  concurrency_buffer = options.host.get("concurrency_buffer")
617
629
  concurrency_buffer_perc = options.host.get("concurrency_buffer_perc")
630
+ scaling_delay = options.host.get("scaling_delay")
618
631
  max_multiplexing = options.host.get("max_multiplexing")
619
632
  base_image = options.host.get("_base_image", None)
620
633
  scheduler = options.host.get("_scheduler", None)
@@ -636,6 +649,7 @@ class FalServerlessHost(Host):
636
649
  min_concurrency=min_concurrency,
637
650
  concurrency_buffer=concurrency_buffer,
638
651
  concurrency_buffer_perc=concurrency_buffer_perc,
652
+ scaling_delay=scaling_delay,
639
653
  request_timeout=request_timeout,
640
654
  startup_timeout=startup_timeout,
641
655
  )
@@ -824,12 +838,14 @@ def function(
824
838
  # FalServerlessHost options
825
839
  metadata: dict[str, Any] | None = None,
826
840
  machine_type: str | list[str] = FAL_SERVERLESS_DEFAULT_MACHINE_TYPE,
841
+ regions: list[str] | None = None,
827
842
  num_gpus: int | None = None,
828
843
  keep_alive: int = FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
829
844
  max_multiplexing: int = FAL_SERVERLESS_DEFAULT_MAX_MULTIPLEXING,
830
845
  min_concurrency: int = FAL_SERVERLESS_DEFAULT_MIN_CONCURRENCY,
831
846
  concurrency_buffer: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER,
832
847
  concurrency_buffer_perc: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER_PERC,
848
+ scaling_delay: int | None = None,
833
849
  request_timeout: int | None = None,
834
850
  startup_timeout: int | None = None,
835
851
  setup_function: Callable[..., None] | None = None,
@@ -855,12 +871,14 @@ def function(
855
871
  # FalServerlessHost options
856
872
  metadata: dict[str, Any] | None = None,
857
873
  machine_type: str | list[str] = FAL_SERVERLESS_DEFAULT_MACHINE_TYPE,
874
+ regions: list[str] | None = None,
858
875
  num_gpus: int | None = None,
859
876
  keep_alive: int = FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
860
877
  max_multiplexing: int = FAL_SERVERLESS_DEFAULT_MAX_MULTIPLEXING,
861
878
  min_concurrency: int = FAL_SERVERLESS_DEFAULT_MIN_CONCURRENCY,
862
879
  concurrency_buffer: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER,
863
880
  concurrency_buffer_perc: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER_PERC,
881
+ scaling_delay: int | None = None,
864
882
  request_timeout: int | None = None,
865
883
  startup_timeout: int | None = None,
866
884
  setup_function: Callable[..., None] | None = None,
@@ -938,12 +956,14 @@ def function(
938
956
  # FalServerlessHost options
939
957
  metadata: dict[str, Any] | None = None,
940
958
  machine_type: str | list[str] = FAL_SERVERLESS_DEFAULT_MACHINE_TYPE,
959
+ regions: list[str] | None = None,
941
960
  num_gpus: int | None = None,
942
961
  keep_alive: int = FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
943
962
  max_multiplexing: int = FAL_SERVERLESS_DEFAULT_MAX_MULTIPLEXING,
944
963
  min_concurrency: int = FAL_SERVERLESS_DEFAULT_MIN_CONCURRENCY,
945
964
  concurrency_buffer: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER,
946
965
  concurrency_buffer_perc: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER_PERC,
966
+ scaling_delay: int | None = None,
947
967
  request_timeout: int | None = None,
948
968
  startup_timeout: int | None = None,
949
969
  setup_function: Callable[..., None] | None = None,
@@ -974,12 +994,14 @@ def function(
974
994
  # FalServerlessHost options
975
995
  metadata: dict[str, Any] | None = None,
976
996
  machine_type: str | list[str] = FAL_SERVERLESS_DEFAULT_MACHINE_TYPE,
997
+ regions: list[str] | None = None,
977
998
  num_gpus: int | None = None,
978
999
  keep_alive: int = FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
979
1000
  max_multiplexing: int = FAL_SERVERLESS_DEFAULT_MAX_MULTIPLEXING,
980
1001
  min_concurrency: int = FAL_SERVERLESS_DEFAULT_MIN_CONCURRENCY,
981
1002
  concurrency_buffer: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER,
982
1003
  concurrency_buffer_perc: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER_PERC,
1004
+ scaling_delay: int | None = None,
983
1005
  request_timeout: int | None = None,
984
1006
  startup_timeout: int | None = None,
985
1007
  setup_function: Callable[..., None] | None = None,
@@ -1004,12 +1026,14 @@ def function(
1004
1026
  # FalServerlessHost options
1005
1027
  metadata: dict[str, Any] | None = None,
1006
1028
  machine_type: str | list[str] = FAL_SERVERLESS_DEFAULT_MACHINE_TYPE,
1029
+ regions: list[str] | None = None,
1007
1030
  num_gpus: int | None = None,
1008
1031
  keep_alive: int = FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
1009
1032
  max_multiplexing: int = FAL_SERVERLESS_DEFAULT_MAX_MULTIPLEXING,
1010
1033
  min_concurrency: int = FAL_SERVERLESS_DEFAULT_MIN_CONCURRENCY,
1011
1034
  concurrency_buffer: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER,
1012
1035
  concurrency_buffer_perc: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER_PERC,
1036
+ scaling_delay: int | None = None,
1013
1037
  request_timeout: int | None = None,
1014
1038
  startup_timeout: int | None = None,
1015
1039
  setup_function: Callable[..., None] | None = None,
@@ -1034,12 +1058,14 @@ def function(
1034
1058
  # FalServerlessHost options
1035
1059
  metadata: dict[str, Any] | None = None,
1036
1060
  machine_type: str | list[str] = FAL_SERVERLESS_DEFAULT_MACHINE_TYPE,
1061
+ regions: list[str] | None = None,
1037
1062
  num_gpus: int | None = None,
1038
1063
  keep_alive: int = FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
1039
1064
  max_multiplexing: int = FAL_SERVERLESS_DEFAULT_MAX_MULTIPLEXING,
1040
1065
  min_concurrency: int = FAL_SERVERLESS_DEFAULT_MIN_CONCURRENCY,
1041
1066
  concurrency_buffer: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER,
1042
1067
  concurrency_buffer_perc: int = FAL_SERVERLESS_DEFAULT_CONCURRENCY_BUFFER_PERC,
1068
+ scaling_delay: int | None = None,
1043
1069
  request_timeout: int | None = None,
1044
1070
  startup_timeout: int | None = None,
1045
1071
  setup_function: Callable[..., None] | None = None,
@@ -1065,6 +1091,9 @@ def function( # type: ignore
1065
1091
  if config.get("image"):
1066
1092
  kind = "container"
1067
1093
 
1094
+ if kind == "container" and config.get("app_files"):
1095
+ raise ValueError("app_files is not supported for container apps.")
1096
+
1068
1097
  options = host.parse_options(kind=kind, **config)
1069
1098
 
1070
1099
  def wrapper(func: Callable[ArgsT, ReturnT]):
@@ -1141,6 +1170,7 @@ class FalFastAPI(FastAPI):
1141
1170
  class RouteSignature(NamedTuple):
1142
1171
  path: str
1143
1172
  is_websocket: bool = False
1173
+ is_health_check: bool = False
1144
1174
  input_modal: type | None = None
1145
1175
  buffering: int | None = None
1146
1176
  session_timeout: float | None = None
fal/api/apps.py CHANGED
@@ -34,7 +34,14 @@ def apps_runners(
34
34
 
35
35
  if state and "all" not in set(state):
36
36
  states = set(state)
37
- alias_runners = [r for r in alias_runners if r.state.value in states]
37
+ alias_runners = [
38
+ r
39
+ for r in alias_runners
40
+ if r.state.value.lower() in states
41
+ or (
42
+ "terminated" in states and r.state.value.lower() == "dead"
43
+ ) # TODO for backwards compatibility. remove later
44
+ ]
38
45
  return alias_runners
39
46
 
40
47
 
@@ -48,6 +55,7 @@ def scale_app(
48
55
  min_concurrency: int | None = None,
49
56
  concurrency_buffer: int | None = None,
50
57
  concurrency_buffer_perc: int | None = None,
58
+ scaling_delay: int | None = None,
51
59
  request_timeout: int | None = None,
52
60
  startup_timeout: int | None = None,
53
61
  machine_types: list[str] | None = None,
@@ -62,8 +70,22 @@ def scale_app(
62
70
  min_concurrency=min_concurrency,
63
71
  concurrency_buffer=concurrency_buffer,
64
72
  concurrency_buffer_perc=concurrency_buffer_perc,
73
+ scaling_delay=scaling_delay,
65
74
  request_timeout=request_timeout,
66
75
  startup_timeout=startup_timeout,
67
76
  machine_types=machine_types,
68
77
  valid_regions=regions,
69
78
  )
79
+
80
+
81
+ def rollout_app(
82
+ client: SyncServerlessClient,
83
+ app_name: str,
84
+ *,
85
+ force: bool = False,
86
+ ) -> None:
87
+ with FalServerlessClient(client._grpc_host, client._credentials).connect() as conn:
88
+ conn.rollout_application(
89
+ application_name=app_name,
90
+ force=force,
91
+ )
fal/api/client.py CHANGED
@@ -2,18 +2,26 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  from dataclasses import dataclass
5
- from typing import List, Optional
5
+ from typing import TYPE_CHECKING, List, Optional
6
6
 
7
7
  from fal.api import FAL_SERVERLESS_DEFAULT_URL, FalServerlessHost
8
8
  from fal.sdk import (
9
9
  AliasInfo,
10
10
  Credentials,
11
+ KeyScope,
11
12
  RunnerInfo,
13
+ ServerlessSecret,
14
+ UserKeyInfo,
12
15
  )
13
16
 
17
+ if TYPE_CHECKING:
18
+ from openapi_fal_rest.client import Client
19
+
14
20
  from . import apps as apps_api
15
21
  from . import deploy as deploy_api
22
+ from . import keys as keys_api
16
23
  from . import runners as runners_api
24
+ from . import secrets as secrets_api
17
25
 
18
26
 
19
27
  class _AppsNamespace:
@@ -38,6 +46,7 @@ class _AppsNamespace:
38
46
  min_concurrency: int | None = None,
39
47
  concurrency_buffer: int | None = None,
40
48
  concurrency_buffer_perc: int | None = None,
49
+ scaling_delay: int | None = None,
41
50
  request_timeout: int | None = None,
42
51
  startup_timeout: int | None = None,
43
52
  machine_types: List[str] | None = None,
@@ -52,12 +61,16 @@ class _AppsNamespace:
52
61
  min_concurrency=min_concurrency,
53
62
  concurrency_buffer=concurrency_buffer,
54
63
  concurrency_buffer_perc=concurrency_buffer_perc,
64
+ scaling_delay=scaling_delay,
55
65
  request_timeout=request_timeout,
56
66
  startup_timeout=startup_timeout,
57
67
  machine_types=machine_types,
58
68
  regions=regions,
59
69
  )
60
70
 
71
+ def rollout(self, app_name: str, *, force: bool = False) -> None:
72
+ return apps_api.rollout_app(self.client, app_name, force=force)
73
+
61
74
 
62
75
  class _RunnersNamespace:
63
76
  def __init__(self, client: SyncServerlessClient):
@@ -73,6 +86,36 @@ class _RunnersNamespace:
73
86
  return runners_api.kill_runner(self.client, runner_id)
74
87
 
75
88
 
89
+ class _KeysNamespace:
90
+ def __init__(self, client: SyncServerlessClient):
91
+ self.client = client
92
+
93
+ def create(
94
+ self, *, scope: KeyScope, description: str | None = None
95
+ ) -> tuple[str, str]:
96
+ return keys_api.create_key(self.client, scope=scope, description=description)
97
+
98
+ def list(self) -> List[UserKeyInfo]:
99
+ return keys_api.list_keys(self.client)
100
+
101
+ def revoke(self, key_id: str) -> None:
102
+ return keys_api.revoke_key(self.client, key_id)
103
+
104
+
105
+ class _SecretsNamespace:
106
+ def __init__(self, client: SyncServerlessClient):
107
+ self.client = client
108
+
109
+ def set(self, name: str, value: str) -> None:
110
+ return secrets_api.set_secret(self.client, name, value)
111
+
112
+ def list(self) -> List[ServerlessSecret]:
113
+ return secrets_api.list_secrets(self.client)
114
+
115
+ def unset(self, name: str) -> None:
116
+ return secrets_api.unset_secret(self.client, name)
117
+
118
+
76
119
  @dataclass
77
120
  class SyncServerlessClient:
78
121
  host: Optional[str] = None
@@ -83,6 +126,8 @@ class SyncServerlessClient:
83
126
  def __post_init__(self) -> None:
84
127
  self.apps = _AppsNamespace(self)
85
128
  self.runners = _RunnersNamespace(self)
129
+ self.keys = _KeysNamespace(self)
130
+ self.secrets = _SecretsNamespace(self)
86
131
 
87
132
  # Top-level verbs
88
133
  def deploy(self, *args, **kwargs):
@@ -93,6 +138,12 @@ class SyncServerlessClient:
93
138
  def _grpc_host(self) -> str:
94
139
  return self.host or FAL_SERVERLESS_DEFAULT_URL
95
140
 
141
+ @property
142
+ def _rest_url(self) -> str:
143
+ from fal.flags import REST_SCHEME
144
+
145
+ return f"{REST_SCHEME}://{self._grpc_host.replace('api', 'rest', 1)}"
146
+
96
147
  @property
97
148
  def _credentials(self) -> Credentials:
98
149
  from fal.sdk import FalServerlessKeyCredentials, get_default_credentials
@@ -127,3 +178,17 @@ class SyncServerlessClient:
127
178
  local_file_path=local_file_path,
128
179
  credentials=self._credentials,
129
180
  )
181
+
182
+ def _create_rest_client(self) -> Client:
183
+ from openapi_fal_rest.client import Client
184
+
185
+ import fal.flags as flags
186
+
187
+ return Client(
188
+ self._rest_url,
189
+ headers=self._credentials.to_headers(),
190
+ timeout=30,
191
+ verify_ssl=not flags.TEST_MODE,
192
+ raise_on_unexpected_status=False,
193
+ follow_redirects=True,
194
+ )
fal/api/deploy.py CHANGED
@@ -12,7 +12,10 @@ if TYPE_CHECKING:
12
12
 
13
13
  import json
14
14
  from collections import namedtuple
15
- from typing import Tuple, Union, cast
15
+ from typing import TYPE_CHECKING, Tuple, Union, cast
16
+
17
+ if TYPE_CHECKING:
18
+ from openapi_fal_rest.client import Client
16
19
 
17
20
  User = namedtuple("User", ["user_id", "username"])
18
21
 
@@ -41,17 +44,16 @@ def _remove_http_and_port_from_url(url):
41
44
  return url
42
45
 
43
46
 
44
- def _get_user() -> User:
47
+ def _get_user(client: Client) -> User:
45
48
  from http import HTTPStatus
46
49
 
47
50
  import openapi_fal_rest.api.users.get_current_user as get_current_user
48
51
 
49
52
  from fal.api import FalServerlessError
50
- from fal.rest_client import REST_CLIENT
51
53
 
52
54
  try:
53
55
  user_details_response = get_current_user.sync_detailed(
54
- client=REST_CLIENT,
56
+ client=client,
55
57
  )
56
58
  except Exception as e:
57
59
  raise FalServerlessError(f"Error fetching user details: {str(e)}")
@@ -102,7 +104,6 @@ def _deploy_from_reference(
102
104
  [file_path] = options
103
105
  file_path = str(file_path) # type: ignore
104
106
 
105
- user = _get_user()
106
107
  host = client._create_host(local_file_path=str(file_path))
107
108
  loaded = load_function_from(
108
109
  host,
@@ -114,7 +115,7 @@ def _deploy_from_reference(
114
115
  app_auth = auth or loaded.app_auth
115
116
  strategy = strategy or "rolling"
116
117
 
117
- app_id = host.register(
118
+ result = host.register(
118
119
  func=isolated_function.func,
119
120
  options=isolated_function.options,
120
121
  application_name=app_name,
@@ -125,17 +126,9 @@ def _deploy_from_reference(
125
126
  scale=scale,
126
127
  )
127
128
 
128
- assert app_id
129
- env_host = _remove_http_and_port_from_url(host.url)
130
- env_host = env_host.replace("api.", "").replace("alpha.", "")
131
-
132
- env_host_parts = env_host.split(".")
133
-
134
- # keep the last 3 parts
135
- playground_host = ".".join(env_host_parts[-3:])
136
-
137
- # just replace .ai for .run
138
- endpoint_host = env_host.replace(".ai", ".run")
129
+ assert result
130
+ assert result.result
131
+ assert result.service_urls
139
132
 
140
133
  urls: dict[str, dict[str, str]] = {
141
134
  "playground": {},
@@ -143,18 +136,12 @@ def _deploy_from_reference(
143
136
  "async": {},
144
137
  }
145
138
  for endpoint in loaded.endpoints:
146
- urls["playground"][endpoint] = (
147
- f"https://{playground_host}/models/{user.username}/{app_name}{endpoint}"
148
- )
149
- urls["sync"][endpoint] = (
150
- f"https://{endpoint_host}/{user.username}/{app_name}{endpoint}"
151
- )
152
- urls["async"][endpoint] = (
153
- f"https://queue.{endpoint_host}/{user.username}/{app_name}{endpoint}"
154
- )
139
+ urls["playground"][endpoint] = f"{result.service_urls.playground}{endpoint}"
140
+ urls["sync"][endpoint] = f"{result.service_urls.run}{endpoint}"
141
+ urls["async"][endpoint] = f"{result.service_urls.queue}{endpoint}"
155
142
 
156
143
  return DeploymentResult(
157
- revision=app_id,
144
+ revision=result.result.application_id,
158
145
  app_name=app_name,
159
146
  urls=urls,
160
147
  )
@@ -186,9 +173,10 @@ def deploy(
186
173
  raise ValueError("Cannot use --app-name or --auth with app name reference.")
187
174
 
188
175
  app_name = app_ref_tuple[0]
189
- app_ref, app_auth, app_strategy, app_scale_settings = get_app_data_from_toml(
176
+ app_ref, app_auth, app_strategy, app_scale_settings, _ = get_app_data_from_toml(
190
177
  app_name
191
178
  )
179
+
192
180
  file_path, func_name = RefAction.split_ref(app_ref)
193
181
 
194
182
  # path/to/myfile.py::MyApp
fal/api/keys.py ADDED
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List
4
+
5
+ if TYPE_CHECKING:
6
+ from fal.sdk import KeyScope, UserKeyInfo
7
+
8
+ from .client import SyncServerlessClient
9
+
10
+
11
+ def create_key(
12
+ client: SyncServerlessClient, *, scope: KeyScope, description: str | None = None
13
+ ) -> tuple[str, str]:
14
+ from fal.sdk import FalServerlessClient
15
+
16
+ with FalServerlessClient(client._grpc_host, client._credentials).connect() as conn:
17
+ return conn.create_user_key(scope, description)
18
+
19
+
20
+ def list_keys(client: SyncServerlessClient) -> List[UserKeyInfo]:
21
+ from fal.sdk import FalServerlessClient
22
+
23
+ with FalServerlessClient(client._grpc_host, client._credentials).connect() as conn:
24
+ return conn.list_user_keys()
25
+
26
+
27
+ def revoke_key(client: SyncServerlessClient, key_id: str) -> None:
28
+ from fal.sdk import FalServerlessClient
29
+
30
+ with FalServerlessClient(client._grpc_host, client._credentials).connect() as conn:
31
+ conn.revoke_user_key(key_id)
fal/api/secrets.py ADDED
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List
4
+
5
+ if TYPE_CHECKING:
6
+ from fal.sdk import ServerlessSecret
7
+
8
+ from .client import SyncServerlessClient
9
+
10
+
11
+ def set_secret(client: SyncServerlessClient, name: str, value: str) -> None:
12
+ from fal.sdk import FalServerlessClient
13
+
14
+ with FalServerlessClient(client._grpc_host, client._credentials).connect() as conn:
15
+ conn.set_secret(name, value)
16
+
17
+
18
+ def list_secrets(client: SyncServerlessClient) -> List[ServerlessSecret]:
19
+ from fal.sdk import FalServerlessClient
20
+
21
+ with FalServerlessClient(client._grpc_host, client._credentials).connect() as conn:
22
+ return list(conn.list_secrets())
23
+
24
+
25
+ def unset_secret(client: SyncServerlessClient, name: str) -> None:
26
+ from fal.sdk import FalServerlessClient
27
+
28
+ with FalServerlessClient(client._grpc_host, client._credentials).connect() as conn:
29
+ conn.delete_secret(name)