Skip to content

Commit

Permalink
Adding edge prediction support
Browse files Browse the repository at this point in the history
  • Loading branch information
olokobayusuf committed Mar 20, 2024
1 parent d7dc761 commit b8fc06b
Show file tree
Hide file tree
Showing 13 changed files with 218 additions and 117 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ dist/
*.egg
*.egg-info/

# Libs
fxn/**/*.dll
fxn/**/*.dylib
fxn/**/*.so

# IDE
.vscode
.vs
Expand Down
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## 0.0.31
+ Added experimental support for making on-device predictions.
+ Added `PredictionResource.name` field for handling prediction resources with required file names.

## 0.0.30
Expand Down
35 changes: 35 additions & 0 deletions edgefxn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#
# Function
# Copyright © 2024 NatML Inc. All Rights Reserved.
#

from argparse import ArgumentParser
from pathlib import Path
from requests import get

parser = ArgumentParser()
parser.add_argument("--version", type=str, default="0.0.13")

def _download_fxnc (name: str, version: str, path: Path):
url = f"https://cdn.fxn.ai/edgefxn/{version}/{name}"
response = get(url)
response.raise_for_status()
with open(path, "wb") as f:
f.write(response.content)

def main (): # CHECK # Linux
args = parser.parse_args()
LIB_PATH_BASE = Path("fxn") / "libs"
_download_fxnc(
"Function-macos.dylib",
args.version,
LIB_PATH_BASE / "macos" / "Function.dylib"
)
_download_fxnc(
"Function-win64.dll",
args.version,
LIB_PATH_BASE / "windows" / "Function.dll"
)

if __name__ == "__main__":
main()
3 changes: 1 addition & 2 deletions fxn/cli/predict.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from rich.progress import Progress, SpinnerColumn, TextColumn
from tempfile import mkstemp
from typer import Argument, Context, Option
from typing import Any, Dict

from ..function import Function
from .auth import get_access_key
Expand All @@ -35,7 +34,7 @@ async def _predict_async (tag: str, context: Context, raw_outputs: bool):
inputs = { context.args[i].replace("-", ""): _parse_value(context.args[i+1]) for i in range(0, len(context.args), 2) }
# Stream
fxn = Function(get_access_key())
async for prediction in fxn.predictions.stream(tag, inputs=inputs, raw_outputs=raw_outputs, return_binary_path=True, verbose=True):
async for prediction in fxn.predictions.stream(tag, inputs=inputs, raw_outputs=raw_outputs, return_binary_path=True):
# Parse results
images = [value for value in prediction.results or [] if isinstance(value, Image.Image)]
prediction.results = [_serialize_value(value) for value in prediction.results] if prediction.results is not None else None
Expand Down
4 changes: 4 additions & 0 deletions fxn/libs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#
# Function
# Copyright © 2024 NatML Inc. All Rights Reserved.
#
4 changes: 4 additions & 0 deletions fxn/libs/linux/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#
# Function
# Copyright © 2024 NatML Inc. All Rights Reserved.
#
4 changes: 4 additions & 0 deletions fxn/libs/macos/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#
# Function
# Copyright © 2024 NatML Inc. All Rights Reserved.
#
4 changes: 4 additions & 0 deletions fxn/libs/windows/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#
# Function
# Copyright © 2024 NatML Inc. All Rights Reserved.
#
82 changes: 48 additions & 34 deletions fxn/services/prediction/fxnc.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ class FXNAcceleration(c_int):
class FXNValue(Structure): pass
class FXNValueMap(Structure): pass
class FXNConfiguration(Structure): pass
class FXNProfile(Structure): pass
class FXNPrediction(Structure): pass
class FXNPredictor(Structure): pass

FXNValueRef = POINTER(FXNValue)
FXNValueMapRef = POINTER(FXNValueMap)
FXNConfigurationRef = POINTER(FXNConfiguration)
FXNProfileRef = POINTER(FXNProfile)
FXNPredictionRef = POINTER(FXNPrediction)
FXNPredictorRef = POINTER(FXNPredictor)

def load_fxnc (path: Path) -> CDLL:
Expand Down Expand Up @@ -94,7 +94,7 @@ def load_fxnc (path: Path) -> CDLL:
fxnc.FXNValueCreateDict.argtypes = [c_char_p, POINTER(FXNValueRef)]
fxnc.FXNValueCreateDict.restype = FXNStatus
# FXNValueCreateImage
fxnc.FXNValueCreateImage.argtypes = [POINTER(c_uint8), c_int32, c_int32, c_int32, FXNValueFlags, POINTER(FXNValueRef)]
fxnc.FXNValueCreateImage.argtypes = [c_void_p, c_int32, c_int32, c_int32, FXNValueFlags, POINTER(FXNValueRef)]
fxnc.FXNValueCreateImage.restype = FXNStatus
# FXNValueMapCreate
fxnc.FXNValueMapCreate.argtypes = [POINTER(FXNValueMapRef)]
Expand Down Expand Up @@ -123,15 +123,18 @@ def load_fxnc (path: Path) -> CDLL:
# FXNConfigurationRelease
fxnc.FXNConfigurationRelease.argtypes = [FXNConfigurationRef]
fxnc.FXNConfigurationRelease.restype = FXNStatus
# FXNConfigurationGetTag
fxnc.FXNConfigurationGetTag.argtypes = [FXNConfigurationRef, c_char_p, c_int32]
fxnc.FXNConfigurationRelease.restype = FXNStatus
# FXNConfigurationSetTag
fxnc.FXNConfigurationSetTag.argtypes = [FXNConfigurationRef, c_char_p]
fxnc.FXNConfigurationSetTag.restype = FXNStatus
# FXNConfigurationGetToken
fxnc.FXNConfigurationGetToken.argtypes = [FXNConfigurationRef, c_char_p, c_int32]
fxnc.FXNConfigurationGetToken.restype = FXNStatus
# FXNConfigurationSetToken
fxnc.FXNConfigurationSetToken.argtypes = [FXNConfigurationRef, c_char_p]
fxnc.FXNConfigurationSetToken.restype = FXNStatus
# FXNConfigurationAddResource
fxnc.FXNConfigurationAddResource.argtypes = [FXNConfigurationRef, c_char_p, c_char_p]
fxnc.FXNConfigurationAddResource.restype = FXNStatus
# FXNConfigurationGetAcceleration
fxnc.FXNConfigurationGetAcceleration.argtypes = [FXNConfigurationRef, POINTER(FXNAcceleration)]
fxnc.FXNConfigurationGetAcceleration.restype = FXNStatus
Expand All @@ -144,40 +147,46 @@ def load_fxnc (path: Path) -> CDLL:
# FXNConfigurationSetDevice
fxnc.FXNConfigurationSetDevice.argtypes = [FXNConfigurationRef, c_void_p]
fxnc.FXNConfigurationSetDevice.restype = FXNStatus
# FXNProfileRelease
fxnc.FXNProfileRelease.argtypes = [FXNProfileRef]
fxnc.FXNProfileRelease.restype = FXNStatus
# FXNProfileGetID
fxnc.FXNProfileGetID.argtypes = [FXNProfileRef, c_char_p, c_int32]
fxnc.FXNProfileGetID.restype = FXNStatus
# FXNProfileGetLatency
fxnc.FXNProfileGetLatency.argtypes = [FXNProfileRef, POINTER(c_double)]
fxnc.FXNProfileGetLatency.restype = FXNStatus
# FXNProfileGetError
fxnc.FXNProfileGetError.argtypes = [FXNProfileRef, c_char_p, c_int32]
fxnc.FXNProfileGetError.restype = FXNStatus
# FXNProfileGetLogs
fxnc.FXNProfileGetLogs.argtypes = [FXNProfileRef, c_char_p, c_int32]
fxnc.FXNProfileGetLogs.restype = FXNStatus
# FXNProfileGetLogLength
fxnc.FXNProfileGetLogLength.argtypes = [FXNProfileRef, POINTER(c_int32)]
fxnc.FXNProfileGetLogLength.restype = FXNStatus
# FXNConfigurationAddResource
fxnc.FXNConfigurationAddResource.argtypes = [FXNConfigurationRef, c_char_p, c_char_p]
fxnc.FXNConfigurationAddResource.restype = FXNStatus
# FXNPredictionRelease
fxnc.FXNPredictionRelease.argtypes = [FXNPredictionRef]
fxnc.FXNPredictionRelease.restype = FXNStatus
# FXNPredictionGetID
fxnc.FXNPredictionGetID.argtypes = [FXNPredictionRef, c_char_p, c_int32]
fxnc.FXNPredictionGetID.restype = FXNStatus
# FXNPredictionGetLatency
fxnc.FXNPredictionGetLatency.argtypes = [FXNPredictionRef, POINTER(c_double)]
fxnc.FXNPredictionGetLatency.restype = FXNStatus
# FXNPredictionGetResults
fxnc.FXNPredictionGetResults.argtypes = [FXNPredictionRef, POINTER(FXNValueMapRef)]
fxnc.FXNPredictionGetResults.restype = FXNStatus
# FXNPredictionGetError
fxnc.FXNPredictionGetError.argtypes = [FXNPredictionRef, c_char_p, c_int32]
fxnc.FXNPredictionGetError.restype = FXNStatus
# FXNPredictionGetLogs
fxnc.FXNPredictionGetLogs.argtypes = [FXNPredictionRef, c_char_p, c_int32]
fxnc.FXNPredictionGetLogs.restype = FXNStatus
# FXNPredictionGetLogLength
fxnc.FXNPredictionGetLogLength.argtypes = [FXNPredictionRef, POINTER(c_int32)]
fxnc.FXNPredictionGetLogLength.restype = FXNStatus
# FXNPredictorCreate
fxnc.FXNPredictorCreate.argtypes = [c_char_p, FXNConfigurationRef, POINTER(FXNPredictorRef)]
fxnc.FXNPredictorCreate.argtypes = [FXNConfigurationRef, POINTER(FXNPredictorRef)]
fxnc.FXNPredictorCreate.restype = FXNStatus
# FXNPredictorRelease
fxnc.FXNPredictorRelease.argtypes = [FXNPredictorRef]
fxnc.FXNPredictorRelease.restype = FXNStatus
# FXNPredictorPredict
fxnc.FXNPredictorPredict.argtypes = [FXNPredictorRef, FXNValueMapRef, POINTER(FXNProfileRef), POINTER(FXNValueMapRef)]
fxnc.FXNPredictorPredict.argtypes = [FXNPredictorRef, FXNValueMapRef, POINTER(FXNPredictionRef)]
fxnc.FXNPredictorPredict.restype = FXNStatus
# FXNGetVersion
fxnc.FXNGetVersion.argtypes = []
fxnc.FXNGetVersion.restype = c_char_p
# Return
return fxnc

def to_fxn_value ( # DEPLOY
def to_fxn_value (
fxnc: CDLL,
value: Union[float, int, bool, str, NDArray, List[Any], Dict[str, Any], Image.Image, bytes, bytearray, memoryview, BytesIO, None],
*,
Expand Down Expand Up @@ -208,17 +217,18 @@ def to_fxn_value ( # DEPLOY
elif isinstance(value, list):
fxnc.FXNValueCreateList(dumps(value).encode(), byref(result))
elif isinstance(value, dict):
fxnc.FXNValueCreateList(dumps(value).encode(), byref(result))
fxnc.FXNValueCreateDict(dumps(value).encode(), byref(result))
elif isinstance(value, Image.Image):
value = array(value)
fxnc.FXNValueCreateImage(
status = fxnc.FXNValueCreateImage(
value.ctypes.data_as(c_void_p),
value.shape[1],
value.shape[0],
value.shape[2],
FXNValueFlags.COPY_DATA,
byref(result)
)
assert status.value == FXNStatus.OK, f"Failed to create image value with status: {status.value}"
elif isinstance(value, (bytes, bytearray, memoryview, BytesIO)):
view = memoryview(value.getvalue() if isinstance(value, BytesIO) else value) if not isinstance(value, memoryview) else value
buffer = (c_uint8 * len(view)).from_buffer(view)
Expand All @@ -232,22 +242,26 @@ def to_fxn_value ( # DEPLOY
raise RuntimeError(f"Failed to convert Python value to Function value because Python value has an unsupported type: {type(value)}")
return result

def to_py_value ( # DEPLOY
def to_py_value (
fxnc: CDLL,
value: type[FXNValueRef]
) -> Union[float, int, bool, str, NDArray, List[Any], Dict[str, Any], Image.Image, BytesIO, None]:
# Type
dtype = FXNDtype()
fxnc.FXNValueGetType(value, byref(dtype))
status = fxnc.FXNValueGetType(value, byref(dtype))
assert status.value == FXNStatus.OK, f"Failed to get value data type with status: {status.value}"
dtype = dtype.value
# Get data
data = c_void_p()
fxnc.FXNValueGetData(value, byref(data))
status = fxnc.FXNValueGetData(value, byref(data))
assert status.value == FXNStatus.OK, f"Failed to get value data with status: {status.value}"
# Get shape
dims = c_int32()
fxnc.FXNValueGetDimensions(value, byref(dims))
status = fxnc.FXNValueGetDimensions(value, byref(dims))
assert status.value == FXNStatus.OK, f"Failed to get value dimensions with status: {status.value}"
shape = zeros(dims.value, dtype=int32)
fxnc.FXNValueGetShape(value, shape.ctypes.data_as(POINTER(c_int32)), dims)
status = fxnc.FXNValueGetShape(value, shape.ctypes.data_as(POINTER(c_int32)), dims)
assert status.value == FXNStatus.OK, f"Failed to get value shape with status: {status.value}"
# Switch
if dtype == FXNDtype.NULL:
return None
Expand Down
Loading

0 comments on commit b8fc06b

Please sign in to comment.