fxn 0.0.36__tar.gz → 0.0.38__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.
- {fxn-0.0.36 → fxn-0.0.38}/PKG-INFO +15 -9
- {fxn-0.0.36 → fxn-0.0.38}/README.md +14 -8
- {fxn-0.0.36 → fxn-0.0.38}/fxn/__init__.py +1 -1
- fxn-0.0.38/fxn/cli/misc.py +32 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/function.py +5 -10
- fxn-0.0.38/fxn/lib/linux/arm64/libFunction.so +0 -0
- fxn-0.0.38/fxn/lib/linux/x86_64/libFunction.so +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/lib/macos/arm64/Function.dylib +0 -0
- fxn-0.0.38/fxn/lib/macos/x86_64/Function.dylib +0 -0
- fxn-0.0.38/fxn/lib/windows/arm64/Function.dll +0 -0
- fxn-0.0.38/fxn/lib/windows/x86_64/Function.dll +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/services/__init__.py +1 -3
- {fxn-0.0.36 → fxn-0.0.38}/fxn/services/prediction.py +29 -30
- {fxn-0.0.36 → fxn-0.0.38}/fxn/services/predictor.py +2 -4
- {fxn-0.0.36 → fxn-0.0.38}/fxn/types/__init__.py +1 -4
- {fxn-0.0.36 → fxn-0.0.38}/fxn/types/predictor.py +4 -4
- fxn-0.0.36/fxn/types/profile.py → fxn-0.0.38/fxn/types/user.py +6 -1
- {fxn-0.0.36 → fxn-0.0.38}/fxn/version.py +1 -1
- {fxn-0.0.36 → fxn-0.0.38}/fxn.egg-info/PKG-INFO +15 -9
- {fxn-0.0.36 → fxn-0.0.38}/fxn.egg-info/SOURCES.txt +2 -7
- fxn-0.0.36/fxn/cli/misc.py +0 -26
- fxn-0.0.36/fxn/lib/macos/x86_64/Function.dylib +0 -0
- fxn-0.0.36/fxn/lib/windows/arm64/Function.dll +0 -0
- fxn-0.0.36/fxn/lib/windows/x86_64/Function.dll +0 -0
- fxn-0.0.36/fxn/magic.py +0 -35
- fxn-0.0.36/fxn/services/environment.py +0 -111
- fxn-0.0.36/fxn/services/storage.py +0 -159
- fxn-0.0.36/fxn/types/environment.py +0 -17
- fxn-0.0.36/fxn/types/storage.py +0 -14
- fxn-0.0.36/fxn/types/tag.py +0 -28
- fxn-0.0.36/fxn/types/user.py +0 -11
- {fxn-0.0.36 → fxn-0.0.38}/LICENSE +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/api/__init__.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/api/client.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/__init__.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/configuration.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/dtype.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/fxnc.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/map.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/prediction.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/predictor.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/status.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/stream.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/value.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/c/version.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/cli/__init__.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/cli/auth.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/cli/env.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/cli/predict.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/cli/predictors.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/lib/__init__.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/services/user.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/types/dtype.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn/types/prediction.py +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn.egg-info/dependency_links.txt +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn.egg-info/entry_points.txt +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn.egg-info/requires.txt +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/fxn.egg-info/top_level.txt +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/pyproject.toml +0 -0
- {fxn-0.0.36 → fxn-0.0.38}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fxn
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.38
|
4
4
|
Summary: Run prediction functions locally in Python. Register at https://fxn.ai.
|
5
5
|
Author-email: "NatML Inc." <hi@fxn.ai>
|
6
6
|
License: Apache License
|
@@ -239,20 +239,24 @@ $ pip install --upgrade fxn
|
|
239
239
|
```
|
240
240
|
|
241
241
|
> [!NOTE]
|
242
|
-
> Function requires Python 3.
|
242
|
+
> Function requires Python 3.10+
|
243
243
|
|
244
244
|
## Retrieving your Access Key
|
245
|
-
Head over to [fxn.ai](https://fxn.ai) to create an account by logging in. Once you do, generate an access key:
|
245
|
+
Head over to [fxn.ai](https://www.fxn.ai/account/developer) to create an account by logging in. Once you do, generate an access key:
|
246
246
|
|
247
247
|

|
248
248
|
|
249
249
|
## Making a Prediction
|
250
|
-
|
250
|
+
First, create a Function client, specifying your access key:
|
251
251
|
```py
|
252
252
|
from fxn import Function
|
253
253
|
|
254
254
|
# Create the Function client
|
255
255
|
fxn = Function(access_key="<Function access key>")
|
256
|
+
```
|
257
|
+
|
258
|
+
Then make a prediction:
|
259
|
+
```py
|
256
260
|
# Create a prediction
|
257
261
|
prediction = fxn.predictions.create(
|
258
262
|
tag="@fxn/greeting",
|
@@ -264,16 +268,18 @@ print(prediction.results[0])
|
|
264
268
|
|
265
269
|
> [!TIP]
|
266
270
|
> Explore public predictors [on Function](https://fxn.ai/explore) or [create your own](https://fxn.ai/waitlist).
|
267
|
-
r
|
268
|
-
## Using the Function CLI
|
269
|
-
Open up a terminal and run the following command:
|
270
271
|
|
272
|
+
## Using the Function CLI
|
273
|
+
Open up a terminal and login to the Function CLI:
|
271
274
|
```sh
|
272
275
|
# Login to Function
|
273
|
-
fxn auth login <ACCESS KEY>
|
276
|
+
$ fxn auth login <ACCESS KEY>
|
277
|
+
```
|
274
278
|
|
279
|
+
Then make a prediction:
|
280
|
+
```sh
|
275
281
|
# Make a prediction using the Function CLI
|
276
|
-
fxn predict @fxn/greeting --name Peter
|
282
|
+
$ fxn predict @fxn/greeting --name Peter
|
277
283
|
```
|
278
284
|
|
279
285
|
___
|
@@ -14,20 +14,24 @@ $ pip install --upgrade fxn
|
|
14
14
|
```
|
15
15
|
|
16
16
|
> [!NOTE]
|
17
|
-
> Function requires Python 3.
|
17
|
+
> Function requires Python 3.10+
|
18
18
|
|
19
19
|
## Retrieving your Access Key
|
20
|
-
Head over to [fxn.ai](https://fxn.ai) to create an account by logging in. Once you do, generate an access key:
|
20
|
+
Head over to [fxn.ai](https://www.fxn.ai/account/developer) to create an account by logging in. Once you do, generate an access key:
|
21
21
|
|
22
22
|

|
23
23
|
|
24
24
|
## Making a Prediction
|
25
|
-
|
25
|
+
First, create a Function client, specifying your access key:
|
26
26
|
```py
|
27
27
|
from fxn import Function
|
28
28
|
|
29
29
|
# Create the Function client
|
30
30
|
fxn = Function(access_key="<Function access key>")
|
31
|
+
```
|
32
|
+
|
33
|
+
Then make a prediction:
|
34
|
+
```py
|
31
35
|
# Create a prediction
|
32
36
|
prediction = fxn.predictions.create(
|
33
37
|
tag="@fxn/greeting",
|
@@ -39,16 +43,18 @@ print(prediction.results[0])
|
|
39
43
|
|
40
44
|
> [!TIP]
|
41
45
|
> Explore public predictors [on Function](https://fxn.ai/explore) or [create your own](https://fxn.ai/waitlist).
|
42
|
-
r
|
43
|
-
## Using the Function CLI
|
44
|
-
Open up a terminal and run the following command:
|
45
46
|
|
47
|
+
## Using the Function CLI
|
48
|
+
Open up a terminal and login to the Function CLI:
|
46
49
|
```sh
|
47
50
|
# Login to Function
|
48
|
-
fxn auth login <ACCESS KEY>
|
51
|
+
$ fxn auth login <ACCESS KEY>
|
52
|
+
```
|
49
53
|
|
54
|
+
Then make a prediction:
|
55
|
+
```sh
|
50
56
|
# Make a prediction using the Function CLI
|
51
|
-
fxn predict @fxn/greeting --name Peter
|
57
|
+
$ fxn predict @fxn/greeting --name Peter
|
52
58
|
```
|
53
59
|
|
54
60
|
___
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#
|
2
|
+
# Function
|
3
|
+
# Copyright © 2024 NatML Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
from rich import print
|
7
|
+
from typer import Exit, Option
|
8
|
+
from webbrowser import open as open_browser
|
9
|
+
|
10
|
+
from ..version import __version__
|
11
|
+
|
12
|
+
def _explore (value: bool):
|
13
|
+
if value:
|
14
|
+
open_browser("https://fxn.ai/explore")
|
15
|
+
raise Exit()
|
16
|
+
|
17
|
+
def _learn (value: bool):
|
18
|
+
if value:
|
19
|
+
open_browser("https://docs.fxn.ai")
|
20
|
+
raise Exit()
|
21
|
+
|
22
|
+
def _version (value: bool):
|
23
|
+
if value:
|
24
|
+
print(__version__)
|
25
|
+
raise Exit()
|
26
|
+
|
27
|
+
def cli_options (
|
28
|
+
explore: bool = Option(None, "--explore", callback=_explore, help="Explore predictors on Function."),
|
29
|
+
learn: bool = Option(None, "--learn", callback=_learn, help="Learn about Function."),
|
30
|
+
version: bool = Option(None, "--version", callback=_version, help="Get the Function CLI version.")
|
31
|
+
):
|
32
|
+
pass
|
@@ -6,7 +6,7 @@
|
|
6
6
|
from os import environ
|
7
7
|
|
8
8
|
from .api import GraphClient
|
9
|
-
from .services import
|
9
|
+
from .services import PredictionService, PredictorService, UserService
|
10
10
|
|
11
11
|
class Function:
|
12
12
|
"""
|
@@ -26,16 +26,11 @@ class Function:
|
|
26
26
|
users: UserService
|
27
27
|
predictors: PredictorService
|
28
28
|
predictions: PredictionService
|
29
|
-
#environment_variables: EnvironmentVariableService
|
30
|
-
#storage: StorageService
|
31
29
|
|
32
30
|
def __init__ (self, access_key: str=None, api_url: str=None):
|
33
31
|
access_key = access_key or environ.get("FXN_ACCESS_KEY", None)
|
34
32
|
api_url = api_url or environ.get("FXN_API_URL", "https://api.fxn.ai")
|
35
|
-
client = GraphClient(access_key, api_url)
|
36
|
-
|
37
|
-
self.
|
38
|
-
self.
|
39
|
-
self.predictors = PredictorService(client, storage)
|
40
|
-
self.predictions = PredictionService(client)
|
41
|
-
#self.environment_variables = EnvironmentVariableService(self.client)
|
33
|
+
self.client = GraphClient(access_key, api_url)
|
34
|
+
self.users = UserService(self.client)
|
35
|
+
self.predictors = PredictorService(self.client)
|
36
|
+
self.predictions = PredictionService(self.client)
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -5,6 +5,4 @@
|
|
5
5
|
|
6
6
|
from .user import UserService, PROFILE_FIELDS, USER_FIELDS
|
7
7
|
from .predictor import PredictorService, PREDICTOR_FIELDS
|
8
|
-
from .prediction import PredictionService, PREDICTION_FIELDS
|
9
|
-
from .environment import EnvironmentVariableService, ENVIRONMENT_VARIABLE_FIELDS
|
10
|
-
from .storage import StorageService
|
8
|
+
from .prediction import PredictionService, PREDICTION_FIELDS
|
@@ -17,6 +17,7 @@ from PIL import Image
|
|
17
17
|
from platform import machine, system
|
18
18
|
from pydantic import BaseModel
|
19
19
|
from requests import get, post
|
20
|
+
from tempfile import gettempdir
|
20
21
|
from typing import Any, AsyncIterator, Dict, List, Optional, Union
|
21
22
|
from urllib.parse import urlparse
|
22
23
|
|
@@ -35,7 +36,7 @@ class PredictionService:
|
|
35
36
|
self,
|
36
37
|
tag: str,
|
37
38
|
*,
|
38
|
-
inputs: Optional[Dict[str,
|
39
|
+
inputs: Optional[Dict[str, ndarray | str | float | int | bool | List[Any] | Dict[str, Any] | Path | Image.Image]] = None,
|
39
40
|
acceleration: Acceleration=Acceleration.Default,
|
40
41
|
client_id: str=None,
|
41
42
|
configuration_id: str=None
|
@@ -58,7 +59,7 @@ class PredictionService:
|
|
58
59
|
return self.__predict(tag=tag, predictor=self.__cache[tag], inputs=inputs)
|
59
60
|
# Query
|
60
61
|
response = post(
|
61
|
-
f"{self.client.api_url}/predict/{tag}
|
62
|
+
f"{self.client.api_url}/predict/{tag}",
|
62
63
|
json={ },
|
63
64
|
headers={
|
64
65
|
"Authorization": f"Bearer {self.client.access_key}",
|
@@ -124,35 +125,27 @@ class PredictionService:
|
|
124
125
|
prediction = self.__predict(tag=tag, predictor=predictor, inputs=inputs)
|
125
126
|
# Yield
|
126
127
|
yield prediction
|
127
|
-
|
128
|
+
|
128
129
|
@classmethod
|
129
130
|
def __load_fxnc (self) -> Optional[CDLL]:
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
if
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
elif os == "Windows":
|
140
|
-
package = f"fxn.lib.windows.{machine()}"
|
141
|
-
resource = f"Function.dll"
|
142
|
-
else:
|
143
|
-
return None
|
144
|
-
# Load
|
131
|
+
os = system().lower()
|
132
|
+
os = "macos" if os == "darwin" else os
|
133
|
+
arch = machine()
|
134
|
+
arch = "arm64" if arch == "aarch64" else arch
|
135
|
+
arch = "x86_64" if arch == "x64" else arch
|
136
|
+
package = f"fxn.lib.{os}.{arch}"
|
137
|
+
resource = "libFunction.so"
|
138
|
+
resource = "Function.dylib" if os == "macos" else resource
|
139
|
+
resource = "Function.dll" if os == "windows" else resource
|
145
140
|
with resources.path(package, resource) as fxnc_path:
|
146
141
|
return load_fxnc(fxnc_path)
|
147
142
|
|
148
143
|
def __get_client_id (self) -> str:
|
149
144
|
# Fallback if fxnc failed to load
|
150
145
|
if not self.__fxnc:
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
"Windows": f"windows-{machine()}"
|
155
|
-
}[system()]
|
146
|
+
os = system().lower()
|
147
|
+
os = "macos" if os == "darwin" else os
|
148
|
+
return f"{os}-{machine()}"
|
156
149
|
# Get
|
157
150
|
buffer = create_string_buffer(64)
|
158
151
|
status = self.__fxnc.FXNConfigurationGetClientID(buffer, len(buffer))
|
@@ -383,7 +376,7 @@ class PredictionService:
|
|
383
376
|
raise RuntimeError(f"Failed to convert Function value to Python value because Function value has unsupported type: {dtype}")
|
384
377
|
|
385
378
|
def __get_resource_path (self, resource: PredictionResource) -> Path:
|
386
|
-
cache_dir =
|
379
|
+
cache_dir = self.__class__.__get_resource_dir() / ".fxn" / "cache"
|
387
380
|
cache_dir.mkdir(exist_ok=True)
|
388
381
|
res_name = Path(urlparse(resource.url).path).name
|
389
382
|
res_path = cache_dir / res_name
|
@@ -394,7 +387,18 @@ class PredictionService:
|
|
394
387
|
with open(res_path, "wb") as f:
|
395
388
|
f.write(req.content)
|
396
389
|
return res_path
|
397
|
-
|
390
|
+
|
391
|
+
@classmethod
|
392
|
+
def __get_resource_dir (cls) -> Path:
|
393
|
+
try:
|
394
|
+
check = Path.home() / ".fxntest"
|
395
|
+
with open(check, "w") as f:
|
396
|
+
f.write("fxn")
|
397
|
+
check.unlink()
|
398
|
+
return Path.home()
|
399
|
+
except:
|
400
|
+
return Path(gettempdir())
|
401
|
+
|
398
402
|
@classmethod
|
399
403
|
def __try_ensure_serializable (cls, object: Any) -> Any:
|
400
404
|
if object is None:
|
@@ -427,11 +431,6 @@ resources {{
|
|
427
431
|
url
|
428
432
|
name
|
429
433
|
}}
|
430
|
-
results {{
|
431
|
-
data
|
432
|
-
type
|
433
|
-
shape
|
434
|
-
}}
|
435
434
|
latency
|
436
435
|
error
|
437
436
|
logs
|
@@ -7,14 +7,12 @@ from typing import List
|
|
7
7
|
|
8
8
|
from ..api import GraphClient
|
9
9
|
from ..types import Predictor, PredictorStatus
|
10
|
-
from .storage import StorageService
|
11
10
|
from .user import PROFILE_FIELDS
|
12
11
|
|
13
12
|
class PredictorService:
|
14
13
|
|
15
|
-
def __init__ (self, client: GraphClient
|
14
|
+
def __init__ (self, client: GraphClient) -> None:
|
16
15
|
self.client = client
|
17
|
-
self.storage = storage
|
18
16
|
|
19
17
|
def retrieve (self, tag: str) -> Predictor:
|
20
18
|
"""
|
@@ -85,7 +83,7 @@ class PredictorService:
|
|
85
83
|
predictors = [Predictor(**predictor) for predictor in predictors]
|
86
84
|
# Return
|
87
85
|
return predictors
|
88
|
-
|
86
|
+
|
89
87
|
def search (
|
90
88
|
self,
|
91
89
|
query: str,
|
@@ -4,9 +4,6 @@
|
|
4
4
|
#
|
5
5
|
|
6
6
|
from .dtype import Dtype
|
7
|
-
from .environment import EnvironmentVariable
|
8
7
|
from .prediction import Prediction, PredictionResource
|
9
8
|
from .predictor import Acceleration, AccessMode, EnumerationMember, Parameter, Predictor, PredictorStatus, Signature
|
10
|
-
from .
|
11
|
-
from .storage import UploadType
|
12
|
-
from .user import User
|
9
|
+
from .user import Profile, User
|
@@ -11,16 +11,16 @@ from pydantic import AliasChoices, BaseModel, ConfigDict, Field
|
|
11
11
|
from typing import Any, Dict, List, Optional, Tuple
|
12
12
|
|
13
13
|
from .dtype import Dtype
|
14
|
-
from .
|
14
|
+
from .user import Profile
|
15
15
|
|
16
16
|
class Acceleration (IntFlag):
|
17
17
|
"""
|
18
18
|
Predictor acceleration.
|
19
19
|
"""
|
20
20
|
Default = 0,
|
21
|
-
CPU
|
22
|
-
GPU
|
23
|
-
NPU
|
21
|
+
CPU = 1 << 0,
|
22
|
+
GPU = 1 << 1,
|
23
|
+
NPU = 1 << 2
|
24
24
|
|
25
25
|
class AccessMode (str, Enum):
|
26
26
|
"""
|
@@ -27,4 +27,9 @@ class Profile (BaseModel):
|
|
27
27
|
avatar: Optional[str] = Field(default=None, description="User avatar URL.")
|
28
28
|
bio: Optional[str] = Field(default=None, description="User bio.")
|
29
29
|
website: Optional[str] = Field(default=None, description="User website.")
|
30
|
-
github: Optional[str] = Field(default=None, description="User GitHub handle.")
|
30
|
+
github: Optional[str] = Field(default=None, description="User GitHub handle.")
|
31
|
+
|
32
|
+
class User (Profile):
|
33
|
+
"""
|
34
|
+
User.
|
35
|
+
"""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fxn
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.38
|
4
4
|
Summary: Run prediction functions locally in Python. Register at https://fxn.ai.
|
5
5
|
Author-email: "NatML Inc." <hi@fxn.ai>
|
6
6
|
License: Apache License
|
@@ -239,20 +239,24 @@ $ pip install --upgrade fxn
|
|
239
239
|
```
|
240
240
|
|
241
241
|
> [!NOTE]
|
242
|
-
> Function requires Python 3.
|
242
|
+
> Function requires Python 3.10+
|
243
243
|
|
244
244
|
## Retrieving your Access Key
|
245
|
-
Head over to [fxn.ai](https://fxn.ai) to create an account by logging in. Once you do, generate an access key:
|
245
|
+
Head over to [fxn.ai](https://www.fxn.ai/account/developer) to create an account by logging in. Once you do, generate an access key:
|
246
246
|
|
247
247
|

|
248
248
|
|
249
249
|
## Making a Prediction
|
250
|
-
|
250
|
+
First, create a Function client, specifying your access key:
|
251
251
|
```py
|
252
252
|
from fxn import Function
|
253
253
|
|
254
254
|
# Create the Function client
|
255
255
|
fxn = Function(access_key="<Function access key>")
|
256
|
+
```
|
257
|
+
|
258
|
+
Then make a prediction:
|
259
|
+
```py
|
256
260
|
# Create a prediction
|
257
261
|
prediction = fxn.predictions.create(
|
258
262
|
tag="@fxn/greeting",
|
@@ -264,16 +268,18 @@ print(prediction.results[0])
|
|
264
268
|
|
265
269
|
> [!TIP]
|
266
270
|
> Explore public predictors [on Function](https://fxn.ai/explore) or [create your own](https://fxn.ai/waitlist).
|
267
|
-
r
|
268
|
-
## Using the Function CLI
|
269
|
-
Open up a terminal and run the following command:
|
270
271
|
|
272
|
+
## Using the Function CLI
|
273
|
+
Open up a terminal and login to the Function CLI:
|
271
274
|
```sh
|
272
275
|
# Login to Function
|
273
|
-
fxn auth login <ACCESS KEY>
|
276
|
+
$ fxn auth login <ACCESS KEY>
|
277
|
+
```
|
274
278
|
|
279
|
+
Then make a prediction:
|
280
|
+
```sh
|
275
281
|
# Make a prediction using the Function CLI
|
276
|
-
fxn predict @fxn/greeting --name Peter
|
282
|
+
$ fxn predict @fxn/greeting --name Peter
|
277
283
|
```
|
278
284
|
|
279
285
|
___
|
@@ -3,7 +3,6 @@ README.md
|
|
3
3
|
pyproject.toml
|
4
4
|
fxn/__init__.py
|
5
5
|
fxn/function.py
|
6
|
-
fxn/magic.py
|
7
6
|
fxn/version.py
|
8
7
|
fxn.egg-info/PKG-INFO
|
9
8
|
fxn.egg-info/SOURCES.txt
|
@@ -31,22 +30,18 @@ fxn/cli/misc.py
|
|
31
30
|
fxn/cli/predict.py
|
32
31
|
fxn/cli/predictors.py
|
33
32
|
fxn/lib/__init__.py
|
33
|
+
fxn/lib/linux/arm64/libFunction.so
|
34
|
+
fxn/lib/linux/x86_64/libFunction.so
|
34
35
|
fxn/lib/macos/arm64/Function.dylib
|
35
36
|
fxn/lib/macos/x86_64/Function.dylib
|
36
37
|
fxn/lib/windows/arm64/Function.dll
|
37
38
|
fxn/lib/windows/x86_64/Function.dll
|
38
39
|
fxn/services/__init__.py
|
39
|
-
fxn/services/environment.py
|
40
40
|
fxn/services/prediction.py
|
41
41
|
fxn/services/predictor.py
|
42
|
-
fxn/services/storage.py
|
43
42
|
fxn/services/user.py
|
44
43
|
fxn/types/__init__.py
|
45
44
|
fxn/types/dtype.py
|
46
|
-
fxn/types/environment.py
|
47
45
|
fxn/types/prediction.py
|
48
46
|
fxn/types/predictor.py
|
49
|
-
fxn/types/profile.py
|
50
|
-
fxn/types/storage.py
|
51
|
-
fxn/types/tag.py
|
52
47
|
fxn/types/user.py
|
fxn-0.0.36/fxn/cli/misc.py
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Function
|
3
|
-
# Copyright © 2024 NatML Inc. All Rights Reserved.
|
4
|
-
#
|
5
|
-
|
6
|
-
from rich import print
|
7
|
-
from typer import Exit, Option
|
8
|
-
from webbrowser import open as open_browser
|
9
|
-
|
10
|
-
from ..version import __version__
|
11
|
-
|
12
|
-
def _learn_callback (value: bool):
|
13
|
-
if value:
|
14
|
-
open_browser("https://docs.fxn.ai")
|
15
|
-
raise Exit()
|
16
|
-
|
17
|
-
def _version_callback (value: bool):
|
18
|
-
if value:
|
19
|
-
print(__version__)
|
20
|
-
raise Exit()
|
21
|
-
|
22
|
-
def cli_options (
|
23
|
-
learn: bool = Option(None, "--learn", callback=_learn_callback, help="Learn about Function."),
|
24
|
-
version: bool = Option(None, "--version", callback=_version_callback, help="Get the Function CLI version.")
|
25
|
-
):
|
26
|
-
pass
|
Binary file
|
Binary file
|
Binary file
|
fxn-0.0.36/fxn/magic.py
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Function
|
3
|
-
# Copyright © 2024 NatML Inc. All Rights Reserved.
|
4
|
-
#
|
5
|
-
|
6
|
-
from IPython.core.interactiveshell import InteractiveShell
|
7
|
-
from IPython.core.magic import Magics, magics_class, line_magic
|
8
|
-
from typing import List
|
9
|
-
|
10
|
-
@magics_class
|
11
|
-
class FunctionMagics (Magics):
|
12
|
-
|
13
|
-
@line_magic
|
14
|
-
def fxn (self, line: str):
|
15
|
-
COMMANDS = {
|
16
|
-
"python": self.__python,
|
17
|
-
"image": self.__image,
|
18
|
-
}
|
19
|
-
args = line.split(" ")
|
20
|
-
command = COMMANDS.get(args[0], None)
|
21
|
-
if command is not None:
|
22
|
-
command(args[1:])
|
23
|
-
else:
|
24
|
-
raise RuntimeError(f"Unrecognized Function command: {args[0]}")
|
25
|
-
|
26
|
-
def __python (self, args: List[str]):
|
27
|
-
version = args[0]
|
28
|
-
print(f"Predictor will use Python {version} when running on Function")
|
29
|
-
|
30
|
-
def __image (self, args: List[str]):
|
31
|
-
image = args[0]
|
32
|
-
print(f"Predictor will use base image {image} when running on Function")
|
33
|
-
|
34
|
-
def load_ipython_extension (ipython: InteractiveShell):
|
35
|
-
ipython.register_magics(FunctionMagics)
|
@@ -1,111 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Function
|
3
|
-
# Copyright © 2024 NatML Inc. All Rights Reserved.
|
4
|
-
#
|
5
|
-
|
6
|
-
from typing import List
|
7
|
-
|
8
|
-
from ..api import GraphClient
|
9
|
-
from ..types import EnvironmentVariable
|
10
|
-
|
11
|
-
class EnvironmentVariableService:
|
12
|
-
|
13
|
-
def __init__ (self, client: GraphClient) -> None:
|
14
|
-
self.client = client
|
15
|
-
self.__value = "xxxxxxxx"
|
16
|
-
|
17
|
-
def list (self, organization: str=None) -> List[EnvironmentVariable]:
|
18
|
-
"""
|
19
|
-
List the current user's environment variables.
|
20
|
-
|
21
|
-
Note that the variable values can only viewed at https://fxn.ai.
|
22
|
-
|
23
|
-
Parameters:
|
24
|
-
organization (str): Organization username.
|
25
|
-
|
26
|
-
Returns:
|
27
|
-
list: User environment variables.
|
28
|
-
"""
|
29
|
-
# Query
|
30
|
-
response = self.client.query(f"""
|
31
|
-
query ($input: UserInput) {{
|
32
|
-
user (input: $input) {{
|
33
|
-
... on User {{
|
34
|
-
environmentVariables {{
|
35
|
-
{ENVIRONMENT_VARIABLE_FIELDS}
|
36
|
-
}}
|
37
|
-
}}
|
38
|
-
... on Organization {{
|
39
|
-
environmentVariables {{
|
40
|
-
{ENVIRONMENT_VARIABLE_FIELDS}
|
41
|
-
}}
|
42
|
-
}}
|
43
|
-
}}
|
44
|
-
}}
|
45
|
-
""",
|
46
|
-
{ "input": { "username": organization } if organization is not None else None }
|
47
|
-
)
|
48
|
-
# Create envs
|
49
|
-
assert response["user"] is not None, "Failed to list environment variables because user could not be found. Check that you are authenticated."
|
50
|
-
environments = response["user"]["environmentVariables"]
|
51
|
-
environments = [EnvironmentVariable(**env, value=self.__value) for env in environments]
|
52
|
-
# Return
|
53
|
-
return environments
|
54
|
-
|
55
|
-
def create (self, name: str, value: str, organization: str=None) -> EnvironmentVariable:
|
56
|
-
"""
|
57
|
-
Create an environment variable.
|
58
|
-
|
59
|
-
This environment variable will apply to all predictors you create.
|
60
|
-
|
61
|
-
Parameters:
|
62
|
-
name (str): Variable name.
|
63
|
-
value (str): Variable value.
|
64
|
-
organization (str): Organization username. Use this for organization environment variables.
|
65
|
-
|
66
|
-
Returns:
|
67
|
-
EnvironmentVariable: Created environment variable.
|
68
|
-
"""
|
69
|
-
# Query
|
70
|
-
response = self.client.query(f"""
|
71
|
-
mutation ($input: CreateEnvironmentVariableInput!) {{
|
72
|
-
environment: createEnvironmentVariable (input: $input) {{
|
73
|
-
{ENVIRONMENT_VARIABLE_FIELDS}
|
74
|
-
}}
|
75
|
-
}}
|
76
|
-
""",
|
77
|
-
{ "input": { "name": name, "value": value, "organization": organization } }
|
78
|
-
)
|
79
|
-
# Create env
|
80
|
-
environment = response["environment"]
|
81
|
-
environment = EnvironmentVariable(**environment, value=self.__value)
|
82
|
-
# Return
|
83
|
-
return environment
|
84
|
-
|
85
|
-
def delete (self, name: str, organization: str=None) -> bool:
|
86
|
-
"""
|
87
|
-
Delete an environment variable.
|
88
|
-
|
89
|
-
Parameters:
|
90
|
-
name (str): Variable name.
|
91
|
-
organization (str): Organization username. Use this for organization environment variables.
|
92
|
-
access_key (str): Function access key.
|
93
|
-
|
94
|
-
Returns:
|
95
|
-
bool: Whether the environment variable was successfully deleted.
|
96
|
-
"""
|
97
|
-
# Query
|
98
|
-
response = self.client.query(f"""
|
99
|
-
mutation ($input: DeleteEnvironmentVariableInput!) {{
|
100
|
-
result: deleteEnvironmentVariable (input: $input)
|
101
|
-
}}
|
102
|
-
""",
|
103
|
-
{ "input": { "name": name, "organization": organization } }
|
104
|
-
)
|
105
|
-
# Return
|
106
|
-
return response["result"]
|
107
|
-
|
108
|
-
|
109
|
-
ENVIRONMENT_VARIABLE_FIELDS = f"""
|
110
|
-
name
|
111
|
-
"""
|
@@ -1,159 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Function
|
3
|
-
# Copyright © 2024 NatML Inc. All Rights Reserved.
|
4
|
-
#
|
5
|
-
|
6
|
-
from base64 import b64encode
|
7
|
-
from io import BytesIO
|
8
|
-
from magika import Magika
|
9
|
-
from pathlib import Path
|
10
|
-
from requests import put
|
11
|
-
from rich.progress import open as open_progress, wrap_file
|
12
|
-
from urllib.parse import urlparse, urlunparse
|
13
|
-
|
14
|
-
from ..api import GraphClient
|
15
|
-
from ..types import UploadType
|
16
|
-
|
17
|
-
class StorageService:
|
18
|
-
|
19
|
-
def __init__ (self, client: GraphClient) -> None:
|
20
|
-
self.client = client
|
21
|
-
|
22
|
-
def create_upload_url (self, name: str, type: UploadType, key: str=None) -> str:
|
23
|
-
"""
|
24
|
-
Create an upload URL.
|
25
|
-
|
26
|
-
Parameters:
|
27
|
-
name (str): File name.
|
28
|
-
type (UploadType): Upload type.
|
29
|
-
key (str): File key. This is useful for grouping related files.
|
30
|
-
|
31
|
-
Returns:
|
32
|
-
str: File upload URL.
|
33
|
-
"""
|
34
|
-
# Query
|
35
|
-
response = self.client.query(f"""
|
36
|
-
mutation ($input: CreateUploadUrlInput!) {{
|
37
|
-
createUploadUrl (input: $input)
|
38
|
-
}}
|
39
|
-
""",
|
40
|
-
{ "input": { "type": type, "name": name, "key": key } }
|
41
|
-
)
|
42
|
-
# Return
|
43
|
-
return response["createUploadUrl"]
|
44
|
-
|
45
|
-
def upload (
|
46
|
-
self,
|
47
|
-
file: str | Path | BytesIO,
|
48
|
-
*,
|
49
|
-
type: UploadType,
|
50
|
-
name: str=None,
|
51
|
-
data_url_limit: int=None,
|
52
|
-
key: str=None,
|
53
|
-
verbose: bool=False
|
54
|
-
) -> str:
|
55
|
-
"""
|
56
|
-
Upload a file and return the URL.
|
57
|
-
|
58
|
-
Parameters:
|
59
|
-
file (str | Path | BytesIO): Input file.
|
60
|
-
type (UploadType): File type.
|
61
|
-
name (str): File name. This MUST be provided if `file` is not a file path.
|
62
|
-
data_url_limit (int): Return a data URL if the file is smaller than this limit (in bytes).
|
63
|
-
key (str): File key. This is useful for grouping related files.
|
64
|
-
verbose (bool): Print a progress bar for the upload.
|
65
|
-
|
66
|
-
Returns:
|
67
|
-
str: Upload URL.
|
68
|
-
"""
|
69
|
-
file = Path(file) if isinstance(file, str) else file
|
70
|
-
if isinstance(file, Path):
|
71
|
-
return self.__upload_file(file, type=type, name=name, key=key, data_url_limit=data_url_limit, verbose=verbose)
|
72
|
-
else:
|
73
|
-
return self.__upload_buffer(file, type=type, name=name, key=key, data_url_limit=data_url_limit, verbose=verbose)
|
74
|
-
|
75
|
-
def __upload_file (
|
76
|
-
self,
|
77
|
-
file: Path,
|
78
|
-
*,
|
79
|
-
type: UploadType,
|
80
|
-
name: str=None,
|
81
|
-
key: str=None,
|
82
|
-
data_url_limit: int=None,
|
83
|
-
verbose: bool=False
|
84
|
-
) -> str:
|
85
|
-
# Check file
|
86
|
-
assert file.exists(), f"Cannot upload {file.name} because the file does not exist"
|
87
|
-
assert file.is_file(), f"Cannot upload {file.name} becaause it does not point to a file"
|
88
|
-
# Create data URL
|
89
|
-
mime = self.__infer_mime(file)
|
90
|
-
if file.stat().st_size < (data_url_limit or 0):
|
91
|
-
with open(file, mode="rb") as f:
|
92
|
-
buffer = BytesIO(f.read())
|
93
|
-
return self.__create_data_url(buffer, mime=mime)
|
94
|
-
# Upload
|
95
|
-
name = name or file.name
|
96
|
-
url = self.create_upload_url(name, type, key=key)
|
97
|
-
with open_progress(file, mode="rb", description=name, disable=not verbose) as f:
|
98
|
-
put(url, data=f, headers={ "Content-Type": mime }).raise_for_status()
|
99
|
-
# Return
|
100
|
-
return self.__simplify_url(url)
|
101
|
-
|
102
|
-
def __upload_buffer (
|
103
|
-
self,
|
104
|
-
file: BytesIO,
|
105
|
-
*,
|
106
|
-
type: UploadType,
|
107
|
-
name: str=None,
|
108
|
-
key: str=None,
|
109
|
-
data_url_limit: int=None,
|
110
|
-
verbose: bool=False
|
111
|
-
) -> str:
|
112
|
-
# Check name
|
113
|
-
assert name, "You must specify the file `name` if the `file` is not a path"
|
114
|
-
# Create data URL
|
115
|
-
file.seek(0)
|
116
|
-
mime = self.__infer_mime(file)
|
117
|
-
size = file.getbuffer().nbytes
|
118
|
-
if size < (data_url_limit or 0):
|
119
|
-
return self.__create_data_url(file, mime=mime)
|
120
|
-
# Upload
|
121
|
-
url = self.create_upload_url(name, type, key=key)
|
122
|
-
with wrap_file(file, total=size, description=name, disable=not verbose) as f:
|
123
|
-
put(url, data=f, headers={ "Content-Type": mime }).raise_for_status()
|
124
|
-
# Return
|
125
|
-
return self.__simplify_url(url)
|
126
|
-
|
127
|
-
def __create_data_url (self, file: BytesIO, *, mime: str) -> str:
|
128
|
-
encoded_data = b64encode(file.getvalue()).decode("ascii")
|
129
|
-
url = f"data:{mime};base64,{encoded_data}"
|
130
|
-
return url
|
131
|
-
|
132
|
-
def __simplify_url (self, url: str) -> str:
|
133
|
-
if url.startswith("data:"):
|
134
|
-
return url
|
135
|
-
parsed_url = urlparse(url)
|
136
|
-
parsed_url = parsed_url._replace(netloc="cdn.fxn.ai", query="")
|
137
|
-
url = urlunparse(parsed_url)
|
138
|
-
return url
|
139
|
-
|
140
|
-
def __infer_mime (self, file: str | Path | BytesIO) -> str:
|
141
|
-
MAGIC_TO_MIME = {
|
142
|
-
b"\x00\x61\x73\x6d": "application/wasm"
|
143
|
-
}
|
144
|
-
# Read magic
|
145
|
-
file = Path(file) if isinstance(file, str) else file
|
146
|
-
if isinstance(file, Path):
|
147
|
-
with open(file, "rb") as f:
|
148
|
-
magic = f.read(4)
|
149
|
-
elif isinstance(file, BytesIO):
|
150
|
-
magic = file.getvalue()[:4]
|
151
|
-
# Check known mime
|
152
|
-
mime = MAGIC_TO_MIME.get(magic)
|
153
|
-
# Infer
|
154
|
-
if mime is None:
|
155
|
-
magika = Magika()
|
156
|
-
result = magika.identify_bytes(file.getvalue()) if isinstance(file, BytesIO) else magika.identify_path(file)
|
157
|
-
mime = result.output.mime_type
|
158
|
-
# Return
|
159
|
-
return mime
|
@@ -1,17 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Function
|
3
|
-
# Copyright © 2024 NatML Inc. All Rights Reserved.
|
4
|
-
#
|
5
|
-
|
6
|
-
from pydantic import BaseModel, Field
|
7
|
-
|
8
|
-
class EnvironmentVariable (BaseModel):
|
9
|
-
"""
|
10
|
-
Predictor environment variable.
|
11
|
-
|
12
|
-
Members:
|
13
|
-
name (str): Variable name.
|
14
|
-
value (str): Variable value.
|
15
|
-
"""
|
16
|
-
name: str = Field(description="Variable name.")
|
17
|
-
value: str = Field(description="Variable value.")
|
fxn-0.0.36/fxn/types/storage.py
DELETED
fxn-0.0.36/fxn/types/tag.py
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Function
|
3
|
-
# Copyright © 2024 NatML Inc. All Rights Reserved.
|
4
|
-
#
|
5
|
-
|
6
|
-
from __future__ import annotations
|
7
|
-
from pydantic import BaseModel, Field
|
8
|
-
|
9
|
-
class Tag (BaseModel):
|
10
|
-
"""
|
11
|
-
Predictor tag.
|
12
|
-
|
13
|
-
Members:
|
14
|
-
username (str): Predictor owner username.
|
15
|
-
name (str): Predictor name.
|
16
|
-
"""
|
17
|
-
username: str = Field(description="Predictor owner username.")
|
18
|
-
name: str = Field(description="Predictor name.")
|
19
|
-
|
20
|
-
def from_str (cls, tag: str) -> Tag:
|
21
|
-
"""
|
22
|
-
Parse a predictor tag from a string.
|
23
|
-
"""
|
24
|
-
username, name = tag.lower()[1:].split("/")
|
25
|
-
return Tag(username=username, name=name)
|
26
|
-
|
27
|
-
def __str__ (self):
|
28
|
-
return f"@{self.username}/{self.name}"
|
fxn-0.0.36/fxn/types/user.py
DELETED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|