Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6f22fa6
modern python
chayim May 16, 2021
2e4bca1
adding nighties
chayim May 16, 2021
6cf5bc5
adding docker file skeleton that builds
chayim May 16, 2021
ef3d224
linter cleanup
chayim May 16, 2021
34c46b5
docs update
chayim May 18, 2021
e5893e2
sample docker, linters, tox updates
chayim May 18, 2021
3ca1c7e
snyk added to the README, poetry pypi workflow
chayim May 18, 2021
1161cb7
add support
AvitalFineRedis May 18, 2021
aa8cff9
add tests
AvitalFineRedis May 18, 2021
044fa97
tox, with poetry
chayim May 18, 2021
24e0fef
ai, from bloom
chayim May 19, 2021
d71fe62
latest
chayim May 19, 2021
b9b31e3
linter comments
chayim May 19, 2021
fbc5cda
lifting dockers
chayim May 19, 2021
db860f6
unifying the pypi settings
chayim May 19, 2021
a313a28
merging master
chayim May 19, 2021
5bf1426
linters
chayim May 19, 2021
d798fb7
Merge branch 'scriptexecute_support' of https://github.com/RedisAI/re…
AvitalFineRedis May 20, 2021
0056bc3
reset
AvitalFineRedis May 20, 2021
d17b278
Some PR fixes
AvitalFineRedis May 20, 2021
cabbdc3
new files
AvitalFineRedis May 20, 2021
1f5011b
Update test-requirements.txt
AvitalFineRedis May 20, 2021
4a2b3a0
Update postprocessor.py
AvitalFineRedis May 20, 2021
a2e4b65
Update setup.py
AvitalFineRedis May 20, 2021
24537c8
Update test.py
AvitalFineRedis May 23, 2021
53b1106
Update client.py
AvitalFineRedis May 25, 2021
2a60e57
add scriptstore, and update scriptexecute & tests
AvitalFineRedis Jul 7, 2021
6ea2a9f
fix examples
AvitalFineRedis Jul 7, 2021
569604e
typo
AvitalFineRedis Jul 7, 2021
918766e
merge again
AvitalFineRedis Jul 7, 2021
7866203
flake8 warnings
AvitalFineRedis Jul 7, 2021
300db9d
flake8 warnings
AvitalFineRedis Jul 7, 2021
b59152e
Merge remote-tracking branch 'origin/scriptexecute_support' into scri…
AvitalFineRedis Jul 7, 2021
12ad6bd
typo
AvitalFineRedis Jul 7, 2021
9a6344a
fix PR comments
AvitalFineRedis Jul 18, 2021
b8e77b9
fix PR comments
AvitalFineRedis Jul 18, 2021
964d8ba
fix imports
AvitalFineRedis Jul 18, 2021
f17dab0
fix imports
AvitalFineRedis Jul 18, 2021
c14248b
Merge remote-tracking branch 'origin/scriptexecute_support' into scri…
AvitalFineRedis Jul 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 120 additions & 3 deletions redisai/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,66 @@ def tensorget(
else processor.tensorget(res, as_numpy, as_numpy_mutable, meta_only)
)

def scriptstore(
self, key: AnyStr, device: str, script: str, entry_points: Union[str, Sequence[str]], tag: AnyStr = None
) -> str:
"""
Set the script to RedisAI. The difference from scriptset is that in scriptstore
you must specify entry points within your script. These functions must have specific
signature: 'def entry_point(tensors: List[Tensor], keys: List[str], args: List[str])'.
RedisAI uses the TorchScript engine to execute the script. So the script should
have only TorchScript supported constructs. That being said, it's important to
mention that using redisai script to do post processing or pre processing for a
Tensorflow (or any other backend) is completely valid. For more details about
TorchScript and supported ops, checkout TorchScript documentation.

Parameters
----------
key : AnyStr
Script key at the server
device : str
Device name. Allowed devices are CPU and GPU. If multiple GPUs are available.
it can be specified using the format GPU:<gpu number>. For example: GPU:0
script : str
Script itself, as a Python string
entry_points : Union[str, Sequence[str]]
A list of functions in the script that may serve as entry point for the
execution. Each entry point must have the specify signature:
def entry_point(tensors: List[Tensor], keys: List[str], args: List[str]))
Note that the script may contain additional helper functions that doesn't
have to follow this signature.
tag : AnyStr
Any string that will be saved in RedisAI as tag for the script

Returns
-------
str
'OK' if success, raise an exception otherwise

Note
----
Even though ``script`` is pure Python code, it's a subset of Python language and not
all the Python operations are supported. For more details, checkout TorchScript
documentation. It's also important to note that that the script is executed on a high
performance C++ runtime instead of the Python interpreter. And hence ``script`` should
not have any import statements (A common mistake people make all the time)

Example
-------
>>> script = r'''
>>> def bar(tensors: List[Tensor], keys: List[str], args: List[str]):
>>> a = tensors[0]
>>> b = tensors[1]
>>> return a + b
>>>'''
>>> con.scriptstore('ket', 'cpu', script, 'bar')
'OK'
"""
args = builder.scriptstore(key, device, script, entry_points, tag)
res = self.execute_command(*args)
return res if not self.enable_postprocess else processor.scriptstore(res)

@deprecated(version="1.2.0", reason="Use scriptstore instead")
def scriptset(
self, key: AnyStr, device: str, script: str, tag: AnyStr = None
) -> str:
Expand Down Expand Up @@ -622,10 +682,11 @@ def scriptdel(self, key: AnyStr) -> str:
res = self.execute_command(*args)
return res if not self.enable_postprocess else processor.scriptdel(res)

@deprecated(version="1.2.0", reason="Use scriptexecute instead")
def scriptrun(
self,
key: AnyStr,
function: AnyStr,
function: str,
inputs: Union[AnyStr, Sequence[AnyStr]],
outputs: Union[AnyStr, Sequence[AnyStr]],
) -> str:
Expand All @@ -636,13 +697,13 @@ def scriptrun(
----------
key : AnyStr
Script key
function : AnyStr
function : str
Name of the function in the ``script``
inputs : Union[AnyStr, List[AnyStr]]
Tensor(s) which is already saved in the RedisAI using a tensorset call. These
tensors will be used as the input for the modelrun
outputs : Union[AnyStr, List[AnyStr]]
keys on which the outputs to be saved. If those keys exist already, modelrun
keys on which the outputs to be saved. If those keys exist already, scriptrun
will overwrite them with new values

Returns
Expand All @@ -659,6 +720,62 @@ def scriptrun(
res = self.execute_command(*args)
return res if not self.enable_postprocess else processor.scriptrun(res)

def scriptexecute(
self,
key: AnyStr,
function: str,
keys: Union[AnyStr, Sequence[AnyStr]] = None,
inputs: Union[AnyStr, Sequence[AnyStr]] = None,
args: Union[AnyStr, Sequence[AnyStr]] = None,
outputs: Union[AnyStr, Sequence[AnyStr]] = None,
timeout: int = None,
) -> str:
"""
Run an already set script. Similar to modelexecute.
Must specify keys or inputs.

Parameters
----------
key : AnyStr
Script key
function : str
Name of the function in the ``script``
keys : Union[AnyStr, Sequence[AnyStr]]
Denotes the list of Redis key names that the script will access to
during its execution, for both read and/or write operations.
inputs : Union[AnyStr, Sequence[AnyStr]]
Denotes the input tensors list.
args : Union[AnyStr, Sequence[AnyStr]]
Denotes the list of additional arguments that a user can send to the
script. All args are sent as strings, but can be casted to other types
supported by torch script, such as int, or float.
outputs : Union[AnyStr, List[AnyStr]]
Denotes the output tensors keys' list. If those keys exist already,
scriptexecute will overwrite them with new values.
timeout : int
The max number on milisecinds that may pass before the request is prossced
(meaning that the result will not be computed after that time and TIMEDOUT
is returned in that case).

Returns
-------
str
'OK' if success, raise an exception otherwise

Example
-------
>>> con.scriptexecute('myscript', 'bar', inputs=['a', 'b'], outputs=['c'])
'OK'
>>> con.scriptexecute('myscript{tag}', 'addn',
>>> inputs=['mytensor1{tag}', 'mytensor2{tag}', 'mytensor3{tag}'],
>>> args=['5.0'],
>>> outputs=['result{tag}'])
'OK'
"""
args = builder.scriptexecute(key, function, keys, inputs, args, outputs, timeout)
res = self.execute_command(*args)
return res if not self.enable_postprocess else processor.scriptexecute(res)

def scriptscan(self) -> List[List[AnyStr]]:
"""
Returns the list of all the script in the RedisAI server. Scriptscan API is
Expand Down
51 changes: 50 additions & 1 deletion redisai/command_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,26 @@ def tensorget(key: AnyStr, as_numpy: bool = True, meta_only: bool = False) -> Se
return args


def scriptstore(
name: AnyStr,
device: str,
script: str,
entry_points: Union[str, Sequence[str]],
tag: AnyStr = None
) -> Sequence:
if device.upper() not in utils.allowed_devices:
raise ValueError(f"Device not allowed. Use any from {utils.allowed_devices}")
if name is None or script is None or entry_points is None:
raise ValueError("Missing required arguments for script store command")
args = ["AI.SCRIPTSTORE", name, device]
if tag:
args += ["TAG", tag]
args += ["ENTRY_POINTS", len(utils.listify(entry_points)), *utils.listify(entry_points)]
args.append("SOURCE")
args.append(script)
return args


def scriptset(name: AnyStr, device: str, script: str, tag: AnyStr = None) -> Sequence:
if device.upper() not in utils.allowed_devices:
raise ValueError(f"Device not allowed. Use any from {utils.allowed_devices}")
Expand All @@ -228,10 +248,12 @@ def scriptdel(name: AnyStr) -> Sequence:

def scriptrun(
name: AnyStr,
function: AnyStr,
function: str,
inputs: Union[AnyStr, Sequence[AnyStr]],
outputs: Union[AnyStr, Sequence[AnyStr]],
) -> Sequence:
if name is None or function is None:
raise ValueError("Missing required arguments for script run command")
args = (
"AI.SCRIPTRUN",
name,
Expand All @@ -244,6 +266,33 @@ def scriptrun(
return args


def scriptexecute(
name: AnyStr,
function: str,
keys: Union[AnyStr, Sequence[AnyStr]],
inputs: Union[AnyStr, Sequence[AnyStr]],
input_args: Union[AnyStr, Sequence[AnyStr]],
outputs: Union[AnyStr, Sequence[AnyStr]],
timeout: int,
) -> Sequence:
if name is None or function is None or (keys is None and inputs is None):
raise ValueError("Missing required arguments for script execute command")
args = ["AI.SCRIPTEXECUTE", name, function]

if keys is not None:
args += ["KEYS", len(utils.listify(keys)), *utils.listify(keys)]
if inputs is not None:
args += ["INPUTS", len(utils.listify(inputs)), *utils.listify(inputs)]
if input_args is not None:
args += ["ARGS", len(utils.listify(input_args)), *utils.listify(input_args)]
if outputs is not None:
args += ["OUTPUTS", len(utils.listify(outputs)), *utils.listify(outputs)]
if timeout is not None:
args += ["TIMEOUT", timeout]

return args


def scriptscan() -> Sequence:
return ("AI._SCRIPTSCAN",)

Expand Down
2 changes: 2 additions & 0 deletions redisai/postprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ def infoget(res):
"modelrun",
"tensorset",
"scriptset",
"scriptstore",
"scriptdel",
"scriptrun",
"scriptexecute",
"inforeset",
)
for fn in decoding_functions:
Expand Down
1 change: 0 additions & 1 deletion redisai/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from typing import AnyStr, ByteString, Callable, List, Sequence, Union

import numpy as np

dtype_dict = {
Expand Down
Loading