clarifai 11.1.5rc1__py3-none-any.whl → 11.1.5rc3__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.
- clarifai/__init__.py +1 -1
- clarifai/cli/__pycache__/model.cpython-310.pyc +0 -0
- clarifai/client/model.py +10 -2
- clarifai/client/model_client.py +53 -27
- clarifai/runners/__pycache__/__init__.cpython-310.pyc +0 -0
- clarifai/runners/dockerfile_template/Dockerfile.template +2 -2
- clarifai/runners/models/__pycache__/base_typed_model.cpython-310.pyc +0 -0
- clarifai/runners/models/__pycache__/model_builder.cpython-310.pyc +0 -0
- clarifai/runners/models/__pycache__/model_class.cpython-310.pyc +0 -0
- clarifai/runners/models/__pycache__/model_runner.cpython-310.pyc +0 -0
- clarifai/runners/models/model_class.py +59 -42
- clarifai/runners/models/model_run_locally.py +3 -78
- clarifai/runners/utils/__pycache__/data_handler.cpython-310.pyc +0 -0
- clarifai/runners/utils/__pycache__/data_types.cpython-310.pyc +0 -0
- clarifai/runners/utils/__pycache__/method_signatures.cpython-310.pyc +0 -0
- clarifai/runners/utils/__pycache__/serializers.cpython-310.pyc +0 -0
- clarifai/runners/utils/data_types.py +334 -0
- clarifai/runners/utils/method_signatures.py +44 -29
- clarifai/runners/utils/serializers.py +1 -1
- {clarifai-11.1.5rc1.dist-info → clarifai-11.1.5rc3.dist-info}/METADATA +1 -1
- {clarifai-11.1.5rc1.dist-info → clarifai-11.1.5rc3.dist-info}/RECORD +25 -23
- {clarifai-11.1.5rc1.dist-info → clarifai-11.1.5rc3.dist-info}/LICENSE +0 -0
- {clarifai-11.1.5rc1.dist-info → clarifai-11.1.5rc3.dist-info}/WHEEL +0 -0
- {clarifai-11.1.5rc1.dist-info → clarifai-11.1.5rc3.dist-info}/entry_points.txt +0 -0
- {clarifai-11.1.5rc1.dist-info → clarifai-11.1.5rc3.dist-info}/top_level.txt +0 -0
clarifai/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "11.1.
|
1
|
+
__version__ = "11.1.5rc3"
|
Binary file
|
clarifai/client/model.py
CHANGED
@@ -443,6 +443,15 @@ class Model(Lister, BaseClient):
|
|
443
443
|
output_config=output_config,
|
444
444
|
)
|
445
445
|
|
446
|
+
def predict2(self, inputs):
|
447
|
+
"""Predicts the model based on the given inputs.
|
448
|
+
|
449
|
+
Args:
|
450
|
+
inputs (list[Input]): The inputs to predict, must be less than 128.
|
451
|
+
"""
|
452
|
+
|
453
|
+
return self.model_client._predict(inputs=inputs,)
|
454
|
+
|
446
455
|
def _check_predict_input_type(self, input_type: str) -> None:
|
447
456
|
"""Checks if the input type is valid for the model.
|
448
457
|
|
@@ -494,8 +503,7 @@ class Model(Lister, BaseClient):
|
|
494
503
|
nodepool_id: str = None,
|
495
504
|
deployment_id: str = None,
|
496
505
|
user_id: str = None):
|
497
|
-
runner_selector =
|
498
|
-
|
506
|
+
runner_selector = None
|
499
507
|
if deployment_id and (compute_cluster_id or nodepool_id):
|
500
508
|
raise UserError(
|
501
509
|
"You can only specify one of deployment_id or compute_cluster_id and nodepool_id.")
|
clarifai/client/model_client.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import inspect
|
1
2
|
import time
|
2
3
|
from typing import Any, Dict, Iterator, List
|
3
4
|
|
@@ -6,7 +7,9 @@ from clarifai_grpc.grpc.api.status import status_code_pb2
|
|
6
7
|
|
7
8
|
from clarifai.constants.model import MAX_MODEL_PREDICT_INPUTS
|
8
9
|
from clarifai.errors import UserError
|
9
|
-
from clarifai.runners.utils.method_signatures import deserialize,
|
10
|
+
from clarifai.runners.utils.method_signatures import (deserialize, get_stream_from_signature,
|
11
|
+
serialize, signatures_from_json,
|
12
|
+
unflatten_nested_keys)
|
10
13
|
from clarifai.utils.misc import BackoffIterator, status_is_retryable
|
11
14
|
|
12
15
|
|
@@ -45,8 +48,10 @@ class ModelClient:
|
|
45
48
|
|
46
49
|
request = service_pb2.PostModelOutputsRequest()
|
47
50
|
request.CopyFrom(self.request_template)
|
48
|
-
request.model.model_version.output_info.params['_method_name'] = '_GET_SIGNATURES'
|
49
|
-
request.inputs.add() # empty input for this method
|
51
|
+
# request.model.model_version.output_info.params['_method_name'] = '_GET_SIGNATURES'
|
52
|
+
inp = request.inputs.add() # empty input for this method
|
53
|
+
inp.data.parts.add() # empty part for this input
|
54
|
+
inp.data.metadata['method_name'] = '_GET_SIGNATURES'
|
50
55
|
start_time = time.time()
|
51
56
|
backoff_iterator = BackoffIterator(10)
|
52
57
|
while True:
|
@@ -56,13 +61,16 @@ class ModelClient:
|
|
56
61
|
self.logger.info(f"Retrying model info fetch with response {response.status!r}")
|
57
62
|
time.sleep(next(backoff_iterator))
|
58
63
|
continue
|
59
|
-
|
60
|
-
if response.status.code != status_code_pb2.SUCCESS:
|
61
|
-
raise Exception(f"Model failed with response {response.status!r}")
|
62
64
|
break
|
65
|
+
if response.status.code == status_code_pb2.INPUT_UNSUPPORTED_FORMAT:
|
66
|
+
# return code from older models that don't support _GET_SIGNATURES
|
67
|
+
self._method_signatures = {}
|
68
|
+
return
|
63
69
|
if response.status.code != status_code_pb2.SUCCESS:
|
64
|
-
raise Exception(response
|
70
|
+
raise Exception(f"Model failed with response {response!r}")
|
65
71
|
self._method_signatures = signatures_from_json(response.outputs[0].data.string_value)
|
72
|
+
import pdb
|
73
|
+
pdb.set_trace()
|
66
74
|
|
67
75
|
def _define_functions(self):
|
68
76
|
'''
|
@@ -105,26 +113,47 @@ class ModelClient:
|
|
105
113
|
# need to bind method_name to the value, not the mutating loop variable
|
106
114
|
f = bind_f(method_name, method_argnames, call_func)
|
107
115
|
|
108
|
-
# set names and docstrings
|
109
|
-
# note we could also have used exec with strings from the signature to define the
|
110
|
-
# function, but this is safer (no xss), and docstrings with the signature is ok enough
|
116
|
+
# set names, annotations and docstrings
|
111
117
|
f.__name__ = method_name
|
112
118
|
f.__qualname__ = f'{self.__class__.__name__}.{method_name}'
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
119
|
+
input_annos = {var.name: var.data_type for var in method_signature.inputs}
|
120
|
+
output_annos = {var.name: var.data_type for var in method_signature.outputs}
|
121
|
+
# unflatten nested keys to match the user function args for docs
|
122
|
+
input_annos = unflatten_nested_keys(input_annos, method_signature.inputs, is_output=False)
|
123
|
+
output_annos = unflatten_nested_keys(output_annos, method_signature.outputs, is_output=True)
|
124
|
+
|
125
|
+
# add Stream[] to the stream input annotations for docs
|
126
|
+
input_stream_argname, _ = get_stream_from_signature(method_signature.inputs)
|
127
|
+
if input_stream_argname:
|
128
|
+
input_annos[input_stream_argname] = 'Stream[' + str(
|
129
|
+
input_annos[input_stream_argname]) + ']'
|
130
|
+
|
131
|
+
# handle multiple outputs in the return annotation
|
132
|
+
return_annotation = output_annos
|
133
|
+
name = next(iter(output_annos.keys()))
|
134
|
+
if len(output_annos) == 1 and name == 'return':
|
118
135
|
# single output
|
119
|
-
|
120
|
-
elif
|
136
|
+
return_annotation = output_annos[name]
|
137
|
+
elif name.startswith('return.') and name.split('.', 1)[1].isnumeric():
|
121
138
|
# tuple output
|
122
|
-
|
139
|
+
return_annotation = '(' + ", ".join(output_annos[f'return.{i}']
|
140
|
+
for i in range(len(output_annos))) + ')'
|
123
141
|
else:
|
124
142
|
# named output
|
125
|
-
|
126
|
-
|
127
|
-
|
143
|
+
return_annotation = f'Output({", ".join(f"{k}={t}" for k, t in output_annos.items())})'
|
144
|
+
if method_signature.method_type in ['generate', 'stream']:
|
145
|
+
return_annotation = f'Stream[{return_annotation}]'
|
146
|
+
|
147
|
+
# set annotations and docstrings
|
148
|
+
sig = inspect.signature(f).replace(
|
149
|
+
parameters=[
|
150
|
+
inspect.Parameter(k, inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=v)
|
151
|
+
for k, v in input_annos.items()
|
152
|
+
],
|
153
|
+
return_annotation=return_annotation,
|
154
|
+
)
|
155
|
+
f.__signature__ = sig
|
156
|
+
f.__doc__ = method_signature.docstring
|
128
157
|
setattr(self, method_name, f)
|
129
158
|
|
130
159
|
def _predict(
|
@@ -190,7 +219,8 @@ class ModelClient:
|
|
190
219
|
if inference_params:
|
191
220
|
request.model.model_version.output_info.params.update(inference_params)
|
192
221
|
if output_config:
|
193
|
-
request.model.model_version.output_info.output_config.
|
222
|
+
request.model.model_version.output_info.output_config.MergeFrom(
|
223
|
+
resources_pb2.OutputConfig(**output_config))
|
194
224
|
|
195
225
|
start_time = time.time()
|
196
226
|
backoff_iterator = BackoffIterator(10)
|
@@ -312,11 +342,7 @@ class ModelClient:
|
|
312
342
|
kwargs = inputs
|
313
343
|
|
314
344
|
# find the streaming vars in the input signature, and the streaming input python param
|
315
|
-
streaming_var_signatures =
|
316
|
-
stream_argname = set([var.name.split('.', 1)[0] for var in streaming_var_signatures])
|
317
|
-
assert len(
|
318
|
-
stream_argname) == 1, 'streaming methods must have exactly one streaming function arg'
|
319
|
-
stream_argname = stream_argname.pop()
|
345
|
+
stream_argname, streaming_var_signatures = get_stream_from_signature(input_signature)
|
320
346
|
|
321
347
|
# get the streaming input generator from the user-provided function arg values
|
322
348
|
user_inputs_generator = kwargs.pop(stream_argname)
|
Binary file
|
@@ -44,8 +44,8 @@ ENV PYTHONPATH=${PYTHONPATH}:/home/nonroot/main \
|
|
44
44
|
CLARIFAI_COMPUTE_CLUSTER_ID=${CLARIFAI_COMPUTE_CLUSTER_ID} \
|
45
45
|
CLARIFAI_API_BASE=${CLARIFAI_API_BASE:-https://api.clarifai.com}
|
46
46
|
|
47
|
-
# Write out the model function signatures
|
48
|
-
RUN ["python", "-m", "clarifai.cli", "model", "signatures", "--model_path", "/home/nonroot/main", "--out_path", "/home/nonroot/main/signatures.yaml"]
|
47
|
+
# # Write out the model function signatures
|
48
|
+
# RUN ["python", "-m", "clarifai.cli", "model", "signatures", "--model_path", "/home/nonroot/main", "--out_path", "/home/nonroot/main/signatures.yaml"]
|
49
49
|
|
50
50
|
# Finally run the clarifai entrypoint to start the runner loop and local dev server.
|
51
51
|
# Note(zeiler): we may want to make this a clarifai CLI call.
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -3,39 +3,73 @@ import itertools
|
|
3
3
|
import logging
|
4
4
|
import os
|
5
5
|
import traceback
|
6
|
-
import types
|
7
6
|
from abc import ABC
|
8
7
|
from typing import Any, Dict, Iterator, List
|
9
8
|
|
10
9
|
from clarifai_grpc.grpc.api import resources_pb2, service_pb2
|
11
10
|
from clarifai_grpc.grpc.api.status import status_code_pb2, status_pb2
|
12
11
|
|
13
|
-
from clarifai.runners.utils import
|
12
|
+
from clarifai.runners.utils import data_types
|
14
13
|
from clarifai.runners.utils.method_signatures import (build_function_signature, deserialize,
|
15
|
-
serialize,
|
14
|
+
get_stream_from_signature, serialize,
|
15
|
+
signatures_to_json)
|
16
16
|
|
17
17
|
_METHOD_INFO_ATTR = '_cf_method_info'
|
18
18
|
|
19
19
|
_RAISE_EXCEPTIONS = os.getenv("RAISE_EXCEPTIONS", "false").lower() == "true"
|
20
20
|
|
21
21
|
|
22
|
+
class methods:
|
23
|
+
'''
|
24
|
+
Decorators to mark methods as predict, generate, or stream methods.
|
25
|
+
'''
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def predict(method):
|
29
|
+
setattr(method, _METHOD_INFO_ATTR, _MethodInfo(method, 'predict'))
|
30
|
+
return method
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
def generate(method):
|
34
|
+
setattr(method, _METHOD_INFO_ATTR, _MethodInfo(method, 'generate'))
|
35
|
+
return method
|
36
|
+
|
37
|
+
@staticmethod
|
38
|
+
def stream(method):
|
39
|
+
setattr(method, _METHOD_INFO_ATTR, _MethodInfo(method, 'stream'))
|
40
|
+
return method
|
41
|
+
|
42
|
+
|
22
43
|
class ModelClass(ABC):
|
44
|
+
'''
|
45
|
+
Base class for model classes that can be run as a service.
|
23
46
|
|
24
|
-
|
25
|
-
|
26
|
-
|
47
|
+
Define methods as predict, generate, or stream methods using the @methods decorators.
|
48
|
+
|
49
|
+
Example:
|
50
|
+
|
51
|
+
from clarifai.runners.model_class import ModelClass, methods
|
52
|
+
from clarifai.runners.utils.data_types import Input, Stream
|
27
53
|
|
28
|
-
|
29
|
-
"""Predict method for single or batched inputs."""
|
30
|
-
raise NotImplementedError("predict() not implemented")
|
54
|
+
class MyModel(ModelClass):
|
31
55
|
|
32
|
-
|
33
|
-
|
34
|
-
|
56
|
+
@methods.predict
|
57
|
+
def predict(self, x: str, y: int) -> List[str]:
|
58
|
+
return [x] * y
|
35
59
|
|
36
|
-
|
37
|
-
|
38
|
-
|
60
|
+
@methods.generate
|
61
|
+
def generate(self, x: str, y: int) -> Stream[str]:
|
62
|
+
for i in range(y):
|
63
|
+
yield x + str(i)
|
64
|
+
|
65
|
+
@methods.stream
|
66
|
+
def stream(self, input_stream: Stream[Input(x=str, y=int)]) -> Stream[str]:
|
67
|
+
for item in input_stream:
|
68
|
+
yield item.x + ' ' + str(item.y)
|
69
|
+
'''
|
70
|
+
|
71
|
+
def load_model(self):
|
72
|
+
"""Load the model."""
|
39
73
|
|
40
74
|
def _handle_get_signatures_request(self) -> service_pb2.MultiOutputResponse:
|
41
75
|
methods = self._get_method_info()
|
@@ -63,7 +97,9 @@ class ModelClass(ABC):
|
|
63
97
|
outputs = []
|
64
98
|
try:
|
65
99
|
# TODO add method name field to proto
|
66
|
-
method_name = request.
|
100
|
+
method_name = request.inputs[0].data.metadata['method_name']
|
101
|
+
# call_params = dict(request.model.model_version.output_info.params)
|
102
|
+
# method_name = call_params.get('_method_name', 'predict')
|
67
103
|
if method_name == '_GET_SIGNATURES': # special case to fetch signatures, TODO add endpoint for this
|
68
104
|
return self._handle_get_signatures_request()
|
69
105
|
if method_name not in self._get_method_info():
|
@@ -96,7 +132,8 @@ class ModelClass(ABC):
|
|
96
132
|
def generate_wrapper(self, request: service_pb2.PostModelOutputsRequest
|
97
133
|
) -> Iterator[service_pb2.MultiOutputResponse]:
|
98
134
|
try:
|
99
|
-
|
135
|
+
call_params = dict(request.model.model_version.output_info.params)
|
136
|
+
method_name = call_params.get('_method_name', 'generate')
|
100
137
|
method = getattr(self, method_name)
|
101
138
|
method_info = method._cf_method_info
|
102
139
|
signature = method_info.signature
|
@@ -133,18 +170,15 @@ class ModelClass(ABC):
|
|
133
170
|
request = next(request_iterator) # get first request to determine method
|
134
171
|
assert len(request.inputs) == 1, "Streaming requires exactly one input"
|
135
172
|
|
136
|
-
|
173
|
+
call_params = dict(request.model.model_version.output_info.params)
|
174
|
+
method_name = call_params.get('_method_name', 'stream')
|
137
175
|
method = getattr(self, method_name)
|
138
176
|
method_info = method._cf_method_info
|
139
177
|
signature = method_info.signature
|
140
178
|
python_param_types = method_info.python_param_types
|
141
179
|
|
142
180
|
# find the streaming vars in the signature
|
143
|
-
streaming_var_signatures =
|
144
|
-
stream_argname = set([var.name.split('.', 1)[0] for var in streaming_var_signatures])
|
145
|
-
assert len(
|
146
|
-
stream_argname) == 1, 'streaming methods must have exactly one streaming function arg'
|
147
|
-
stream_argname = stream_argname.pop()
|
181
|
+
stream_argname, streaming_var_signatures = get_stream_from_signature(signature.inputs)
|
148
182
|
|
149
183
|
# convert all inputs for the first request, including the first stream value
|
150
184
|
inputs = self._convert_input_protos_to_python(request.inputs, signature.inputs,
|
@@ -190,7 +224,7 @@ class ModelClass(ABC):
|
|
190
224
|
for k, v in kwargs.items():
|
191
225
|
if k not in python_param_types:
|
192
226
|
continue
|
193
|
-
kwargs[k] =
|
227
|
+
kwargs[k] = data_types.cast(v, python_param_types[k])
|
194
228
|
result.append(kwargs)
|
195
229
|
return result
|
196
230
|
|
@@ -203,6 +237,7 @@ class ModelClass(ABC):
|
|
203
237
|
if not isinstance(output, dict): # TODO Output type, not just dict
|
204
238
|
output = {'return': output}
|
205
239
|
serialize(output, variables_signature, proto.data, is_output=True)
|
240
|
+
proto.status.code = status_code_pb2.SUCCESS
|
206
241
|
return proto
|
207
242
|
|
208
243
|
@classmethod
|
@@ -245,21 +280,3 @@ class _MethodInfo:
|
|
245
280
|
if p.annotation != inspect.Parameter.empty
|
246
281
|
}
|
247
282
|
self.python_param_types.pop('self', None)
|
248
|
-
|
249
|
-
|
250
|
-
def predict(method):
|
251
|
-
setattr(method, _METHOD_INFO_ATTR, _MethodInfo(method, 'predict'))
|
252
|
-
return method
|
253
|
-
|
254
|
-
|
255
|
-
def generate(method):
|
256
|
-
setattr(method, _METHOD_INFO_ATTR, _MethodInfo(method, 'generate'))
|
257
|
-
return method
|
258
|
-
|
259
|
-
|
260
|
-
def stream(method):
|
261
|
-
setattr(method, _METHOD_INFO_ATTR, _MethodInfo(method, 'stream'))
|
262
|
-
return method
|
263
|
-
|
264
|
-
|
265
|
-
methods = types.SimpleNamespace(predict=predict, generate=generate, stream=stream)
|
@@ -7,14 +7,11 @@ import subprocess
|
|
7
7
|
import sys
|
8
8
|
import tempfile
|
9
9
|
import time
|
10
|
-
import traceback
|
11
10
|
import venv
|
12
11
|
|
13
12
|
from clarifai_grpc.grpc.api import resources_pb2, service_pb2
|
14
|
-
from clarifai_grpc.grpc.api.status import status_code_pb2, status_pb2
|
15
13
|
|
16
14
|
from clarifai.runners.models.model_builder import ModelBuilder
|
17
|
-
from clarifai.runners.utils.url_fetcher import ensure_urls_downloaded
|
18
15
|
from clarifai.utils.logging import logger
|
19
16
|
|
20
17
|
|
@@ -111,85 +108,13 @@ class ModelRunLocally:
|
|
111
108
|
for i in range(1):
|
112
109
|
yield request
|
113
110
|
|
114
|
-
def _run_model_inference(self, model):
|
115
|
-
"""Perform inference using the model."""
|
116
|
-
request = self._build_request()
|
117
|
-
stream_request = self._build_stream_request()
|
118
|
-
|
119
|
-
ensure_urls_downloaded(request)
|
120
|
-
predict_response = None
|
121
|
-
generate_response = None
|
122
|
-
stream_response = None
|
123
|
-
try:
|
124
|
-
predict_response = model.predict(request)
|
125
|
-
except NotImplementedError:
|
126
|
-
logger.info("Model does not implement predict() method.")
|
127
|
-
except Exception as e:
|
128
|
-
logger.error(f"Model Prediction failed: {e}")
|
129
|
-
traceback.print_exc()
|
130
|
-
predict_response = service_pb2.MultiOutputResponse(status=status_pb2.Status(
|
131
|
-
code=status_code_pb2.MODEL_PREDICTION_FAILED,
|
132
|
-
description="Prediction failed",
|
133
|
-
details="",
|
134
|
-
internal_details=str(e),
|
135
|
-
))
|
136
|
-
|
137
|
-
if predict_response:
|
138
|
-
if predict_response.outputs[0].status.code != status_code_pb2.SUCCESS:
|
139
|
-
logger.error(f"Moddel Prediction failed: {predict_response}")
|
140
|
-
else:
|
141
|
-
logger.info(f"Model Prediction succeeded: {predict_response}")
|
142
|
-
|
143
|
-
try:
|
144
|
-
generate_response = model.generate(request)
|
145
|
-
except NotImplementedError:
|
146
|
-
logger.info("Model does not implement generate() method.")
|
147
|
-
except Exception as e:
|
148
|
-
logger.error(f"Model Generation failed: {e}")
|
149
|
-
traceback.print_exc()
|
150
|
-
generate_response = service_pb2.MultiOutputResponse(status=status_pb2.Status(
|
151
|
-
code=status_code_pb2.MODEL_GENERATION_FAILED,
|
152
|
-
description="Generation failed",
|
153
|
-
details="",
|
154
|
-
internal_details=str(e),
|
155
|
-
))
|
156
|
-
|
157
|
-
if generate_response:
|
158
|
-
generate_first_res = next(generate_response)
|
159
|
-
if generate_first_res.outputs[0].status.code != status_code_pb2.SUCCESS:
|
160
|
-
logger.error(f"Moddel Prediction failed: {generate_first_res}")
|
161
|
-
else:
|
162
|
-
logger.info(
|
163
|
-
f"Model Prediction succeeded for generate and first response: {generate_first_res}")
|
164
|
-
|
165
|
-
try:
|
166
|
-
stream_response = model.stream(stream_request)
|
167
|
-
except NotImplementedError:
|
168
|
-
logger.info("Model does not implement stream() method.")
|
169
|
-
except Exception as e:
|
170
|
-
logger.error(f"Model Stream failed: {e}")
|
171
|
-
traceback.print_exc()
|
172
|
-
stream_response = service_pb2.MultiOutputResponse(status=status_pb2.Status(
|
173
|
-
code=status_code_pb2.MODEL_STREAM_FAILED,
|
174
|
-
description="Stream failed",
|
175
|
-
details="",
|
176
|
-
internal_details=str(e),
|
177
|
-
))
|
178
|
-
|
179
|
-
if stream_response:
|
180
|
-
stream_first_res = next(stream_response)
|
181
|
-
if stream_first_res.outputs[0].status.code != status_code_pb2.SUCCESS:
|
182
|
-
logger.error(f"Moddel Prediction failed: {stream_first_res}")
|
183
|
-
else:
|
184
|
-
logger.info(
|
185
|
-
f"Model Prediction succeeded for stream and first response: {stream_first_res}")
|
186
|
-
|
187
111
|
def _run_test(self):
|
188
112
|
"""Test the model locally by making a prediction."""
|
189
113
|
# Create the model
|
190
114
|
model = self.builder.create_model_instance()
|
191
|
-
#
|
192
|
-
|
115
|
+
# call its test method, if it has one
|
116
|
+
if hasattr(model, "test"):
|
117
|
+
model.test()
|
193
118
|
|
194
119
|
def test_model(self):
|
195
120
|
"""Test the model by running it locally in the virtual environment."""
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,334 @@
|
|
1
|
+
import io
|
2
|
+
from typing import Iterable, List, get_args, get_origin
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
from clarifai_grpc.grpc.api.resources_pb2 import Audio as AudioProto
|
6
|
+
from clarifai_grpc.grpc.api.resources_pb2 import Concept as ConceptProto
|
7
|
+
from clarifai_grpc.grpc.api.resources_pb2 import Frame as FrameProto
|
8
|
+
from clarifai_grpc.grpc.api.resources_pb2 import Image as ImageProto
|
9
|
+
from clarifai_grpc.grpc.api.resources_pb2 import Region as RegionProto
|
10
|
+
from clarifai_grpc.grpc.api.resources_pb2 import Text as TextProto
|
11
|
+
from clarifai_grpc.grpc.api.resources_pb2 import Video as VideoProto
|
12
|
+
from PIL import Image as PILImage
|
13
|
+
|
14
|
+
|
15
|
+
class MessageData:
|
16
|
+
|
17
|
+
def to_proto(self):
|
18
|
+
raise NotImplementedError
|
19
|
+
|
20
|
+
@classmethod
|
21
|
+
def from_proto(cls, proto):
|
22
|
+
raise NotImplementedError
|
23
|
+
|
24
|
+
def cast(self, python_type):
|
25
|
+
if python_type == self.__class__:
|
26
|
+
return self
|
27
|
+
raise TypeError(f'Incompatible type for {self.__class__.__name__}: {python_type}')
|
28
|
+
|
29
|
+
|
30
|
+
class Output(dict):
|
31
|
+
__getattr__ = dict.__getitem__
|
32
|
+
__setattr__ = dict.__setitem__
|
33
|
+
|
34
|
+
def __origin__(self):
|
35
|
+
return self
|
36
|
+
|
37
|
+
def __args__(self):
|
38
|
+
return list(self.keys())
|
39
|
+
|
40
|
+
|
41
|
+
class Input(dict):
|
42
|
+
__getattr__ = dict.__getitem__
|
43
|
+
__setattr__ = dict.__setitem__
|
44
|
+
|
45
|
+
def __origin__(self):
|
46
|
+
return self
|
47
|
+
|
48
|
+
def __args__(self):
|
49
|
+
return list(self.keys())
|
50
|
+
|
51
|
+
|
52
|
+
class Stream(Iterable):
|
53
|
+
pass
|
54
|
+
|
55
|
+
|
56
|
+
class Text(MessageData):
|
57
|
+
|
58
|
+
def __init__(self, text: str, url: str = None):
|
59
|
+
self.text = text
|
60
|
+
self.url = url
|
61
|
+
|
62
|
+
def to_proto(self) -> TextProto:
|
63
|
+
return TextProto(raw=self.text or '', url=self.url or '')
|
64
|
+
|
65
|
+
@classmethod
|
66
|
+
def from_proto(cls, proto: TextProto) -> "Text":
|
67
|
+
return cls(proto.raw, proto.url or None)
|
68
|
+
|
69
|
+
def cast(self, python_type):
|
70
|
+
if python_type == str:
|
71
|
+
return self.text
|
72
|
+
if python_type == Text:
|
73
|
+
return self
|
74
|
+
raise TypeError(f'Incompatible type for Text: {python_type}')
|
75
|
+
|
76
|
+
|
77
|
+
class Concept(MessageData):
|
78
|
+
|
79
|
+
def __init__(self, name: str, value: float = 0):
|
80
|
+
self.name = name
|
81
|
+
self.value = value
|
82
|
+
|
83
|
+
def __repr__(self) -> str:
|
84
|
+
return f"Concept(name={self.name!r}, value={self.value})"
|
85
|
+
|
86
|
+
def to_proto(self):
|
87
|
+
return ConceptProto(name=self.name, value=self.value)
|
88
|
+
|
89
|
+
@classmethod
|
90
|
+
def from_proto(cls, proto: ConceptProto) -> "Concept":
|
91
|
+
return cls(proto.name, proto.value)
|
92
|
+
|
93
|
+
|
94
|
+
class Region(MessageData):
|
95
|
+
|
96
|
+
def __init__(self, proto_region: RegionProto):
|
97
|
+
self.proto = proto_region
|
98
|
+
|
99
|
+
@property
|
100
|
+
def box(self) -> List[float]:
|
101
|
+
bbox = self.proto.region_info.bounding_box
|
102
|
+
return [bbox.left_col, bbox.top_row, bbox.right_col, bbox.bottom_row] # x1, y1, x2, y2
|
103
|
+
|
104
|
+
@box.setter
|
105
|
+
def box(self, value: List[float]):
|
106
|
+
bbox = self.proto.region_info.bounding_box
|
107
|
+
bbox.left_col, bbox.top_row, bbox.right_col, bbox.bottom_row = value
|
108
|
+
|
109
|
+
@property
|
110
|
+
def concepts(self) -> List[Concept]:
|
111
|
+
return [Concept.from_proto(proto) for proto in self.proto.data.concepts]
|
112
|
+
|
113
|
+
@concepts.setter
|
114
|
+
def concepts(self, value: List[Concept]):
|
115
|
+
self.proto.data.concepts.extend([concept.to_proto() for concept in value])
|
116
|
+
|
117
|
+
def __repr__(self) -> str:
|
118
|
+
return f"Region(box={self.box}, concepts={self.concepts})"
|
119
|
+
|
120
|
+
def to_proto(self) -> RegionProto:
|
121
|
+
return self.proto
|
122
|
+
|
123
|
+
@classmethod
|
124
|
+
def from_proto(cls, proto: RegionProto) -> "Region":
|
125
|
+
return cls(proto)
|
126
|
+
|
127
|
+
|
128
|
+
class Image(MessageData):
|
129
|
+
|
130
|
+
def __init__(self, proto_image: ImageProto = None, url: str = None, bytes: bytes = None):
|
131
|
+
if proto_image is None:
|
132
|
+
proto_image = ImageProto()
|
133
|
+
self.proto = proto_image
|
134
|
+
# use setters for init vals
|
135
|
+
if url:
|
136
|
+
self.url = url
|
137
|
+
if bytes:
|
138
|
+
self.bytes = bytes
|
139
|
+
|
140
|
+
@property
|
141
|
+
def url(self) -> str:
|
142
|
+
return self.proto.url
|
143
|
+
|
144
|
+
@url.setter
|
145
|
+
def url(self, value: str):
|
146
|
+
self.proto.url = value
|
147
|
+
|
148
|
+
@property
|
149
|
+
def bytes(self) -> bytes:
|
150
|
+
return self.proto.base64
|
151
|
+
|
152
|
+
@bytes.setter
|
153
|
+
def bytes(self, value: bytes):
|
154
|
+
self.proto.base64 = value
|
155
|
+
|
156
|
+
def __repr__(self) -> str:
|
157
|
+
attrs = []
|
158
|
+
if self.url:
|
159
|
+
attrs.append(f"url={self.url!r}")
|
160
|
+
if self.bytes:
|
161
|
+
attrs.append(f"bytes=<{len(self.bytes)} bytes>")
|
162
|
+
return f"Image({', '.join(attrs)})"
|
163
|
+
|
164
|
+
@classmethod
|
165
|
+
def from_url(cls, url: str) -> "Image":
|
166
|
+
proto_image = ImageProto(url=url)
|
167
|
+
return cls(proto_image)
|
168
|
+
|
169
|
+
@classmethod
|
170
|
+
def from_pil(cls, pil_image: PILImage.Image) -> "Image":
|
171
|
+
with io.BytesIO() as output:
|
172
|
+
pil_image.save(output, format="PNG")
|
173
|
+
image_bytes = output.getvalue()
|
174
|
+
proto_image = ImageProto(base64=image_bytes)
|
175
|
+
return cls(proto_image)
|
176
|
+
|
177
|
+
def to_pil(self) -> PILImage.Image:
|
178
|
+
if not self.proto.base64:
|
179
|
+
raise ValueError("Image has no bytes")
|
180
|
+
return PILImage.open(io.BytesIO(self.proto.base64))
|
181
|
+
|
182
|
+
def to_numpy(self) -> np.ndarray:
|
183
|
+
return np.asarray(self.to_pil())
|
184
|
+
|
185
|
+
def to_proto(self) -> ImageProto:
|
186
|
+
return self.proto
|
187
|
+
|
188
|
+
@classmethod
|
189
|
+
def from_proto(cls, proto: ImageProto) -> "Image":
|
190
|
+
return cls(proto)
|
191
|
+
|
192
|
+
def cast(self, python_type):
|
193
|
+
if python_type == Image:
|
194
|
+
return self
|
195
|
+
if python_type in (PILImage.Image, PILImage):
|
196
|
+
return self.to_pil()
|
197
|
+
if python_type == np.ndarray or get_origin(python_type) == np.ndarray:
|
198
|
+
return self.to_numpy()
|
199
|
+
raise TypeError(f'Incompatible type for Image: {python_type}')
|
200
|
+
|
201
|
+
|
202
|
+
class Audio(MessageData):
|
203
|
+
|
204
|
+
def __init__(self, proto_audio: AudioProto):
|
205
|
+
self.proto = proto_audio
|
206
|
+
|
207
|
+
@property
|
208
|
+
def url(self) -> str:
|
209
|
+
return self.proto.url
|
210
|
+
|
211
|
+
@url.setter
|
212
|
+
def url(self, value: str):
|
213
|
+
self.proto.url = value
|
214
|
+
|
215
|
+
@property
|
216
|
+
def bytes(self) -> bytes:
|
217
|
+
return self.proto.base64
|
218
|
+
|
219
|
+
@bytes.setter
|
220
|
+
def bytes(self, value: bytes):
|
221
|
+
self.proto.base64 = value
|
222
|
+
|
223
|
+
@classmethod
|
224
|
+
def from_url(cls, url: str) -> "Audio":
|
225
|
+
proto_audio = AudioProto(url=url)
|
226
|
+
return cls(proto_audio)
|
227
|
+
|
228
|
+
def __repr__(self) -> str:
|
229
|
+
attrs = []
|
230
|
+
if self.url:
|
231
|
+
attrs.append(f"url={self.url!r}")
|
232
|
+
if self.bytes:
|
233
|
+
attrs.append(f"bytes=<{len(self.bytes)} bytes>")
|
234
|
+
return f"Audio({', '.join(attrs)})"
|
235
|
+
|
236
|
+
def to_proto(self) -> AudioProto:
|
237
|
+
return self.proto
|
238
|
+
|
239
|
+
@classmethod
|
240
|
+
def from_proto(cls, proto: AudioProto) -> "Audio":
|
241
|
+
return cls(proto)
|
242
|
+
|
243
|
+
|
244
|
+
class Frame(MessageData):
|
245
|
+
|
246
|
+
def __init__(self, proto_frame: FrameProto):
|
247
|
+
self.proto = proto_frame
|
248
|
+
|
249
|
+
@property
|
250
|
+
def time(self) -> float:
|
251
|
+
# TODO: time is a uint32, so this will overflow at 49.7 days
|
252
|
+
# we should be using double or uint64 in the proto instead
|
253
|
+
return self.proto.frame_info.time / 1000.0
|
254
|
+
|
255
|
+
@time.setter
|
256
|
+
def time(self, value: float):
|
257
|
+
self.proto.frame_info.time = int(value * 1000)
|
258
|
+
|
259
|
+
@property
|
260
|
+
def image(self) -> Image:
|
261
|
+
return Image.from_proto(self.proto.data.image)
|
262
|
+
|
263
|
+
@image.setter
|
264
|
+
def image(self, value: Image):
|
265
|
+
self.proto.data.image.CopyFrom(value.to_proto())
|
266
|
+
|
267
|
+
@property
|
268
|
+
def regions(self) -> List[Region]:
|
269
|
+
return [Region(region) for region in self.proto.data.regions]
|
270
|
+
|
271
|
+
@regions.setter
|
272
|
+
def regions(self, value: List[Region]):
|
273
|
+
self.proto.data.regions.extend([region.proto for region in value])
|
274
|
+
|
275
|
+
def to_proto(self) -> FrameProto:
|
276
|
+
return self.proto
|
277
|
+
|
278
|
+
@classmethod
|
279
|
+
def from_proto(cls, proto: FrameProto) -> "Frame":
|
280
|
+
return cls(proto)
|
281
|
+
|
282
|
+
|
283
|
+
class Video(MessageData):
|
284
|
+
|
285
|
+
def __init__(self, proto_video: VideoProto):
|
286
|
+
self.proto = proto_video
|
287
|
+
|
288
|
+
@property
|
289
|
+
def url(self) -> str:
|
290
|
+
return self.proto.url
|
291
|
+
|
292
|
+
@url.setter
|
293
|
+
def url(self, value: str):
|
294
|
+
self.proto.url = value
|
295
|
+
|
296
|
+
@property
|
297
|
+
def bytes(self) -> bytes:
|
298
|
+
return self.proto.base64
|
299
|
+
|
300
|
+
@bytes.setter
|
301
|
+
def bytes(self, value: bytes):
|
302
|
+
self.proto.base64 = value
|
303
|
+
|
304
|
+
@classmethod
|
305
|
+
def from_url(cls, url: str) -> "Video":
|
306
|
+
proto_video = VideoProto(url=url)
|
307
|
+
return cls(proto_video)
|
308
|
+
|
309
|
+
def __repr__(self) -> str:
|
310
|
+
attrs = []
|
311
|
+
if self.url:
|
312
|
+
attrs.append(f"url={self.url!r}")
|
313
|
+
if self.bytes:
|
314
|
+
attrs.append(f"bytes=<{len(self.bytes)} bytes>")
|
315
|
+
return f"Video({', '.join(attrs)})"
|
316
|
+
|
317
|
+
def to_proto(self) -> VideoProto:
|
318
|
+
return self.proto
|
319
|
+
|
320
|
+
@classmethod
|
321
|
+
def from_proto(cls, proto: VideoProto) -> "Video":
|
322
|
+
return cls(proto)
|
323
|
+
|
324
|
+
|
325
|
+
def cast(value, python_type):
|
326
|
+
list_type = (get_origin(python_type) == list)
|
327
|
+
if isinstance(value, MessageData):
|
328
|
+
return value.cast(python_type)
|
329
|
+
if list_type and isinstance(value, np.ndarray):
|
330
|
+
return value.tolist()
|
331
|
+
if list_type and isinstance(value, list):
|
332
|
+
inner_type = get_args(python_type)[0]
|
333
|
+
return [cast(item, inner_type) for item in value]
|
334
|
+
return value
|
@@ -2,7 +2,7 @@ import inspect
|
|
2
2
|
import json
|
3
3
|
import re
|
4
4
|
import types
|
5
|
-
from collections import namedtuple
|
5
|
+
from collections import OrderedDict, namedtuple
|
6
6
|
from typing import List, get_args, get_origin
|
7
7
|
|
8
8
|
import numpy as np
|
@@ -11,7 +11,7 @@ import yaml
|
|
11
11
|
from clarifai_grpc.grpc.api import resources_pb2
|
12
12
|
from google.protobuf.message import Message as MessageProto
|
13
13
|
|
14
|
-
from clarifai.runners.utils import
|
14
|
+
from clarifai.runners.utils import data_types
|
15
15
|
from clarifai.runners.utils.serializers import (AtomicFieldSerializer, ImageSerializer,
|
16
16
|
ListSerializer, MessageSerializer,
|
17
17
|
NDArraySerializer, NullValueSerializer, Serializer)
|
@@ -33,7 +33,7 @@ def build_function_signature(func, method_type: str):
|
|
33
33
|
raise ValueError('Function must have a return annotation')
|
34
34
|
# check for multiple return values and convert to dict for named values
|
35
35
|
return_streaming = False
|
36
|
-
if get_origin(return_annotation) ==
|
36
|
+
if get_origin(return_annotation) == data_types.Stream:
|
37
37
|
return_annotation = get_args(return_annotation)[0]
|
38
38
|
return_streaming = True
|
39
39
|
if get_origin(return_annotation) == tuple:
|
@@ -73,7 +73,7 @@ def build_function_signature(func, method_type: str):
|
|
73
73
|
input_stream_vars = [var for var in input_vars if var.streaming]
|
74
74
|
if len(input_stream_vars) == 0:
|
75
75
|
raise TypeError('Stream methods must include a Stream input')
|
76
|
-
if
|
76
|
+
if not all(var.streaming for var in output_vars):
|
77
77
|
raise TypeError('Stream methods must return a single Stream')
|
78
78
|
else:
|
79
79
|
raise TypeError('Invalid method type: %s' % method_type)
|
@@ -85,6 +85,7 @@ def build_function_signature(func, method_type: str):
|
|
85
85
|
#method_signature.method_type = getattr(resources_pb2.RunnerMethodType, method_type)
|
86
86
|
assert method_type in ('predict', 'generate', 'stream')
|
87
87
|
method_signature.method_type = method_type
|
88
|
+
method_signature.docstring = func.__doc__
|
88
89
|
|
89
90
|
#method_signature.inputs.extend(input_vars)
|
90
91
|
#method_signature.outputs.extend(output_vars)
|
@@ -163,7 +164,7 @@ def serialize(kwargs, signatures, proto=None, is_output=False):
|
|
163
164
|
if proto is None:
|
164
165
|
proto = resources_pb2.Data()
|
165
166
|
if not is_output: # TODO: use this consistently for return keys also
|
166
|
-
flatten_nested_keys(kwargs, signatures, is_output)
|
167
|
+
kwargs = flatten_nested_keys(kwargs, signatures, is_output)
|
167
168
|
unknown = set(kwargs.keys()) - set(sig.name for sig in signatures)
|
168
169
|
if unknown:
|
169
170
|
if unknown == {'return'} and len(signatures) > 1:
|
@@ -204,8 +205,8 @@ def deserialize(proto, signatures, is_output=False):
|
|
204
205
|
return kwargs['return']
|
205
206
|
if kwargs and 'return.0' in kwargs: # case for tuple return values
|
206
207
|
return tuple(kwargs[f'return.{i}'] for i in range(len(kwargs)))
|
207
|
-
return
|
208
|
-
unflatten_nested_keys(kwargs, signatures, is_output)
|
208
|
+
return data_types.Output(kwargs)
|
209
|
+
kwargs = unflatten_nested_keys(kwargs, signatures, is_output)
|
209
210
|
return kwargs
|
210
211
|
|
211
212
|
|
@@ -238,18 +239,32 @@ def unflatten_nested_keys(kwargs, signatures, is_output):
|
|
238
239
|
Unflatten nested keys in kwargs into a dict, e.g. {'a.b': 1} -> {'a': {'b': 1}}
|
239
240
|
Uses the signatures to determine which keys are nested.
|
240
241
|
The dict subclass is Input or Output, depending on the is_output flag.
|
242
|
+
Preserves the order of args from the signatures.
|
241
243
|
'''
|
244
|
+
unflattened = OrderedDict()
|
242
245
|
for sig in signatures:
|
243
246
|
if '.' not in sig.name:
|
247
|
+
if sig.name in kwargs:
|
248
|
+
unflattened[sig.name] = kwargs[sig.name]
|
244
249
|
continue
|
245
250
|
if sig.name not in kwargs:
|
246
251
|
continue
|
247
252
|
parts = sig.name.split('.')
|
248
253
|
assert len(parts) == 2, 'Only one level of nested keys is supported'
|
249
|
-
if parts[0] not in
|
250
|
-
|
251
|
-
|
252
|
-
return
|
254
|
+
if parts[0] not in unflattened:
|
255
|
+
unflattened[parts[0]] = data_types.Output() if is_output else data_types.Input()
|
256
|
+
unflattened[parts[0]][parts[1]] = kwargs[sig.name]
|
257
|
+
return unflattened
|
258
|
+
|
259
|
+
|
260
|
+
def get_stream_from_signature(signatures):
|
261
|
+
streaming_signatures = [var for var in signatures if var.streaming]
|
262
|
+
if not streaming_signatures:
|
263
|
+
return None, []
|
264
|
+
stream_argname = set([var.name.split('.', 1)[0] for var in streaming_signatures])
|
265
|
+
assert len(stream_argname) == 1, 'streaming methods must have exactly one streaming function arg'
|
266
|
+
stream_argname = stream_argname.pop()
|
267
|
+
return stream_argname, streaming_signatures
|
253
268
|
|
254
269
|
|
255
270
|
def _is_empty_proto_data(data):
|
@@ -314,20 +329,20 @@ def _normalize_types(param, is_output=False):
|
|
314
329
|
tp = param.annotation
|
315
330
|
|
316
331
|
# stream type indicates streaming, not part of the data itself
|
317
|
-
streaming = (get_origin(tp) ==
|
332
|
+
streaming = (get_origin(tp) == data_types.Stream)
|
318
333
|
if streaming:
|
319
334
|
tp = get_args(tp)[0]
|
320
335
|
|
321
336
|
if is_output or streaming: # named types can be used for outputs or streaming inputs
|
322
337
|
# output type used for named return values, each with their own data type
|
323
|
-
if isinstance(tp, (dict,
|
338
|
+
if isinstance(tp, (dict, data_types.Output, data_types.Input)):
|
324
339
|
return {param.name + '.' + name: _normalize_data_type(val)
|
325
340
|
for name, val in tp.items()}, streaming
|
326
|
-
if tp ==
|
341
|
+
if tp == data_types.Output: # check for Output type without values
|
327
342
|
if not is_output:
|
328
343
|
raise TypeError('Output types can only be used for output values')
|
329
344
|
raise TypeError('Output types must be instantiated with inner type values for each key')
|
330
|
-
if tp ==
|
345
|
+
if tp == data_types.Input: # check for Output type without values
|
331
346
|
if is_output:
|
332
347
|
raise TypeError('Input types can only be used for input values')
|
333
348
|
raise TypeError(
|
@@ -349,7 +364,7 @@ def _normalize_data_type(tp):
|
|
349
364
|
# check for PIL images (sometimes types use the module, sometimes the class)
|
350
365
|
# set these to use the Image data handler
|
351
366
|
if tp in (PIL.Image, PIL.Image.Image):
|
352
|
-
tp =
|
367
|
+
tp = data_types.Image
|
353
368
|
|
354
369
|
# put back list
|
355
370
|
if is_list:
|
@@ -388,20 +403,20 @@ _DATA_TYPES = {
|
|
388
403
|
_DataType('None', '', NullValueSerializer()),
|
389
404
|
np.ndarray:
|
390
405
|
_DataType('ndarray', 'ndarray', NDArraySerializer()),
|
391
|
-
|
392
|
-
_DataType('Text', 'text', MessageSerializer(
|
393
|
-
|
406
|
+
data_types.Text:
|
407
|
+
_DataType('Text', 'text', MessageSerializer(data_types.Text)),
|
408
|
+
data_types.Image:
|
394
409
|
_DataType('Image', 'image', ImageSerializer()),
|
395
|
-
|
396
|
-
_DataType('Concept', 'concepts', MessageSerializer(
|
397
|
-
|
398
|
-
_DataType('Region', 'regions', MessageSerializer(
|
399
|
-
|
400
|
-
_DataType('Frame', 'frames', MessageSerializer(
|
401
|
-
|
402
|
-
_DataType('Audio', 'audio', MessageSerializer(
|
403
|
-
|
404
|
-
_DataType('Video', 'video', MessageSerializer(
|
410
|
+
data_types.Concept:
|
411
|
+
_DataType('Concept', 'concepts', MessageSerializer(data_types.Concept)),
|
412
|
+
data_types.Region:
|
413
|
+
_DataType('Region', 'regions', MessageSerializer(data_types.Region)),
|
414
|
+
data_types.Frame:
|
415
|
+
_DataType('Frame', 'frames', MessageSerializer(data_types.Frame)),
|
416
|
+
data_types.Audio:
|
417
|
+
_DataType('Audio', 'audio', MessageSerializer(data_types.Audio)),
|
418
|
+
data_types.Video:
|
419
|
+
_DataType('Video', 'video', MessageSerializer(data_types.Video)),
|
405
420
|
|
406
421
|
# lists handled specially, not as generic lists using parts
|
407
422
|
List[int]:
|
@@ -4,7 +4,7 @@ import numpy as np
|
|
4
4
|
from clarifai_grpc.grpc.api import resources_pb2
|
5
5
|
from PIL import Image as PILImage
|
6
6
|
|
7
|
-
from clarifai.runners.utils.
|
7
|
+
from clarifai.runners.utils.data_types import Image, MessageData
|
8
8
|
|
9
9
|
|
10
10
|
class Serializer:
|
@@ -1,4 +1,4 @@
|
|
1
|
-
clarifai/__init__.py,sha256=
|
1
|
+
clarifai/__init__.py,sha256=cQU5SXsYznf1FLV9LeeLMrjUc1vlOnCbQLM5_Z--QKI,26
|
2
2
|
clarifai/cli.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
clarifai/errors.py,sha256=RwzTajwds51wLD0MVlMC5kcpBnzRpreDLlazPSBZxrg,2605
|
4
4
|
clarifai/versions.py,sha256=jctnczzfGk_S3EnVqb2FjRKfSREkNmvNEwAAa_VoKiQ,222
|
@@ -19,7 +19,7 @@ clarifai/cli/__pycache__/__main__.cpython-310.pyc,sha256=CM9FOqcSyt-DLnck7FovGDx
|
|
19
19
|
clarifai/cli/__pycache__/base.cpython-310.pyc,sha256=a7mN_r511p_Ao8aXrWU9KPSe8wkDZJW4p8UhlD6EoB8,3151
|
20
20
|
clarifai/cli/__pycache__/compute_cluster.cpython-310.pyc,sha256=NHLAcVEwqUhci0KB5DpnPWUqXcCttpWrA3F5zld4qN8,1985
|
21
21
|
clarifai/cli/__pycache__/deployment.cpython-310.pyc,sha256=AhfbPlwjjj_TmC2UayjuRbNr00dOukDl6NfLhm2rIng,2278
|
22
|
-
clarifai/cli/__pycache__/model.cpython-310.pyc,sha256=
|
22
|
+
clarifai/cli/__pycache__/model.cpython-310.pyc,sha256=1boWk53FmE7FjNVWSH-azhFxPqzoDbDmtu30eHUxFYY,9075
|
23
23
|
clarifai/cli/__pycache__/nodepool.cpython-310.pyc,sha256=nEmM-s1HFg7xM5x-bpnqHFWAVLWj0J0ZskYtd6NSq20,2473
|
24
24
|
clarifai/client/__init__.py,sha256=xI1U0l5AZdRThvQAXCLsd9axxyFzXXJ22m8LHqVjQRU,662
|
25
25
|
clarifai/client/app.py,sha256=6pckYme1urV2YJjLIYfeZ-vH0Z5YSQa51jzIMcEfwug,38342
|
@@ -29,8 +29,8 @@ clarifai/client/dataset.py,sha256=y3zKT_VhP1gyN3OO-b3cPeW21ZXyKbQ7ZJkEG06bsTU,32
|
|
29
29
|
clarifai/client/deployment.py,sha256=w7Y6pA1rYG4KRK1SwusRZc2sQRXlG8wezuVdzSWpCo0,2586
|
30
30
|
clarifai/client/input.py,sha256=obMAHMDU1OwfXZ8KraOnGFlWzlW-3F7Ob_2lcOQMlhY,46339
|
31
31
|
clarifai/client/lister.py,sha256=03KGMvs5RVyYqxLsSrWhNc34I8kiF1Ph0NeyEwu7nMU,2082
|
32
|
-
clarifai/client/model.py,sha256=
|
33
|
-
clarifai/client/model_client.py,sha256=
|
32
|
+
clarifai/client/model.py,sha256=Z4cz-qXznbttETnH3W_LyyscGawvtBkRfaSCsdOgAvs,75334
|
33
|
+
clarifai/client/model_client.py,sha256=vPdNZbLV8IaTDhyi6dUV_WoPx6we-EFq5gX4MufAAD4,16653
|
34
34
|
clarifai/client/module.py,sha256=FTkm8s9m-EaTKN7g9MnLhGJ9eETUfKG7aWZ3o1RshYs,4204
|
35
35
|
clarifai/client/nodepool.py,sha256=la3vTFrO4LX8zm2eQ5jqf2L0-kQ63Dano8FibadoZbk,10152
|
36
36
|
clarifai/client/search.py,sha256=GaPWN6JmTQGZaCHr6U1yv0zqR6wKFl7i9IVLg2ul1CI,14254
|
@@ -128,41 +128,43 @@ clarifai/rag/__pycache__/rag.cpython-310.pyc,sha256=_xylc1DvMu1KYcDr-dtfUU2uzUjL
|
|
128
128
|
clarifai/rag/__pycache__/utils.cpython-310.pyc,sha256=IDUXR7Iv4KfHSY3sbx_bgPdJQn2ozyRCuz01EUTmCUw,3694
|
129
129
|
clarifai/runners/__init__.py,sha256=cDJ31l41dDsqW4Xn6sFMkKxxdIMTnGH9IW6sVkq0TNw,207
|
130
130
|
clarifai/runners/server.py,sha256=xHDLdhQApCgYG19QOKXqJNCGNyw1Vsvobq3UdryDrVc,4132
|
131
|
-
clarifai/runners/__pycache__/__init__.cpython-310.pyc,sha256=
|
131
|
+
clarifai/runners/__pycache__/__init__.cpython-310.pyc,sha256=jVTodJyP53yeIEk2sPufcafNVxxCWiHOD50my0nwFcw,365
|
132
132
|
clarifai/runners/__pycache__/server.cpython-310.pyc,sha256=HfZ6_Vrr5q78nznFqP_aWhPENplHIol_qPaf8lLlotE,3229
|
133
133
|
clarifai/runners/dockerfile_template/Dockerfile.debug,sha256=sRlfRmSLE_TiLORcVRx-3-B0vvSNeUYgm0CCrWmLvAA,667
|
134
134
|
clarifai/runners/dockerfile_template/Dockerfile.debug~,sha256=7YOVg3adIaiudfSkfLGeyxt-FfIBbD3UIIYccrIVJTs,426
|
135
|
-
clarifai/runners/dockerfile_template/Dockerfile.template,sha256=
|
135
|
+
clarifai/runners/dockerfile_template/Dockerfile.template,sha256=MWGjj0My6d18v-nyhWuNbSXUzCzR6KWkohRB1yInb7Q,2525
|
136
136
|
clarifai/runners/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
137
137
|
clarifai/runners/models/model_builder.py,sha256=UCAeJiP_fRFfIuVI1sOge7LMwe-psgvdBuZ0LsJQMpM,33062
|
138
|
-
clarifai/runners/models/model_class.py,sha256=
|
139
|
-
clarifai/runners/models/model_run_locally.py,sha256=
|
138
|
+
clarifai/runners/models/model_class.py,sha256=7pZ8ci4M8Qh91OW-C6yuWAhl3psLDfyvGypaGyFRkLs,11122
|
139
|
+
clarifai/runners/models/model_run_locally.py,sha256=VZetm9Mko8MBjcjwr6PCnTU9gF3glgD5qvpbj-8tW2s,17962
|
140
140
|
clarifai/runners/models/model_runner.py,sha256=qyc73pe4xc9BsUKHwnOyC9g-RNCARiFis4GTh-yg0vg,6219
|
141
141
|
clarifai/runners/models/model_servicer.py,sha256=A--b1P71PBCAMJCpy_-fpNDkfCVdvdMh1LleW15dSas,3037
|
142
142
|
clarifai/runners/models/__pycache__/__init__.cpython-310.pyc,sha256=GTFjzypyx5wnDGqxYeY01iya1CELKb5fOFBFLV031yU,171
|
143
|
-
clarifai/runners/models/__pycache__/base_typed_model.cpython-310.pyc,sha256=
|
144
|
-
clarifai/runners/models/__pycache__/model_builder.cpython-310.pyc,sha256=
|
145
|
-
clarifai/runners/models/__pycache__/model_class.cpython-310.pyc,sha256=
|
143
|
+
clarifai/runners/models/__pycache__/base_typed_model.cpython-310.pyc,sha256=rr_Pxrq9MaT1keVVxBHF59pKv8-dX53B2uLscDXfbfY,8271
|
144
|
+
clarifai/runners/models/__pycache__/model_builder.cpython-310.pyc,sha256=0C76O8ZOXrT3y6CVs53h_9cg7XkZKbSxyd6zp0UoAug,27487
|
145
|
+
clarifai/runners/models/__pycache__/model_class.cpython-310.pyc,sha256=0pXfvIxFEOhPMscFYUT3Up6KuVOy9Hw_Aj3B2rx-q0k,9498
|
146
146
|
clarifai/runners/models/__pycache__/model_run_locally.cpython-310.pyc,sha256=maLshBrvhTy0C6grT26JPAA14P-nD9WDWN25E2BErsE,16976
|
147
|
-
clarifai/runners/models/__pycache__/model_runner.cpython-310.pyc,sha256=
|
147
|
+
clarifai/runners/models/__pycache__/model_runner.cpython-310.pyc,sha256=u4ZYB_TJJgvqkQ-7_yPUoKU0tT2ycwzBXuqfORwUF3Y,4999
|
148
148
|
clarifai/runners/models/__pycache__/model_servicer.cpython-310.pyc,sha256=9QTYk4zVHBUtlTfkH3WaE6frNdN6ba57EVj4bKe18YA,2501
|
149
149
|
clarifai/runners/models/__pycache__/model_upload.cpython-310.pyc,sha256=erkIUHtJv1ohMOFT8EAJuVfBsEY4o0GxpeXrZqjrfGk,21046
|
150
150
|
clarifai/runners/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
151
151
|
clarifai/runners/utils/const.py,sha256=bwj-Pcw558-pasdIFbNhnkn-9oiCdojYH1fNTTUG2gU,1048
|
152
152
|
clarifai/runners/utils/data_handler.py,sha256=zCA_C3mbxgMroqrygMa5xsyuMF476mgd697MUPlOR0E,8327
|
153
|
+
clarifai/runners/utils/data_types.py,sha256=5_1S0tJEqhW8BY0ohREVyfsyObPMLkN7H78n7mQ_4Rs,8322
|
153
154
|
clarifai/runners/utils/data_utils.py,sha256=R1iQ82TuQ9JwxCJk8yEB1Lyb0BYVhVbWJI9YDi1zGOs,318
|
154
155
|
clarifai/runners/utils/loader.py,sha256=SgNHMwRmCCymFQm8aDp73NmIUHhM-N60CBlTKbPzmVc,7470
|
155
|
-
clarifai/runners/utils/method_signatures.py,sha256=
|
156
|
-
clarifai/runners/utils/serializers.py,sha256=
|
156
|
+
clarifai/runners/utils/method_signatures.py,sha256=HHaU_KYxtZxwGD_1tAU1K6dYsTtOEcfl892ApovcGmw,17408
|
157
|
+
clarifai/runners/utils/serializers.py,sha256=NLLR3E40qbw2vJ1psKyo9vUL56jKIJMwDM3C6JRGvaI,3926
|
157
158
|
clarifai/runners/utils/url_fetcher.py,sha256=v_8JOWmkyFAzsBulsieKX7Nfjy1Yg7wGSZeqfEvw2cg,1640
|
158
159
|
clarifai/runners/utils/__pycache__/__init__.cpython-310.pyc,sha256=PRPZOzUV5Z8grWizu5RKOkki0iLYxZDJBgsLfmCcieE,170
|
159
160
|
clarifai/runners/utils/__pycache__/const.cpython-310.pyc,sha256=EBpjmzlqWBxRGqu_KXeVx80uDslhufrErs57SbLr3DE,953
|
160
|
-
clarifai/runners/utils/__pycache__/data_handler.cpython-310.pyc,sha256=
|
161
|
+
clarifai/runners/utils/__pycache__/data_handler.cpython-310.pyc,sha256=0rmmZ0AKL0z2ahf90g0xnqRy2GvgbSRkNYE9lSzSn_Q,7961
|
162
|
+
clarifai/runners/utils/__pycache__/data_types.cpython-310.pyc,sha256=RdIGmYmmh0LmmiLMgSQRT7K6A8BKvVb9O4F3RJ9AE_4,12385
|
161
163
|
clarifai/runners/utils/__pycache__/data_utils.cpython-310.pyc,sha256=1e6NiK6bnJiiAo2KPsDmm91BSlbI3mVkQZKbDfh5hBI,642
|
162
164
|
clarifai/runners/utils/__pycache__/loader.cpython-310.pyc,sha256=X1MwgLanVXLs-QLot1X145A36W29ONXZRZe5q6_jARo,7319
|
163
165
|
clarifai/runners/utils/__pycache__/logging.cpython-310.pyc,sha256=VV0KFcnuYpFFtaG4EeDIgg7c4QEsBLo-eX_NnsyFEhA,331
|
164
|
-
clarifai/runners/utils/__pycache__/method_signatures.cpython-310.pyc,sha256=
|
165
|
-
clarifai/runners/utils/__pycache__/serializers.cpython-310.pyc,sha256=
|
166
|
+
clarifai/runners/utils/__pycache__/method_signatures.cpython-310.pyc,sha256=CLfkuYN95CQXad8D9lrb1ZOZ340uWfVZ2_-CgHs_WTQ,13678
|
167
|
+
clarifai/runners/utils/__pycache__/serializers.cpython-310.pyc,sha256=5D-aRO-GTdkAF-6gRFHE_fPyxOZRH4aE6wq9FZeEP0M,5275
|
166
168
|
clarifai/runners/utils/__pycache__/url_fetcher.cpython-310.pyc,sha256=jFxVdOmm7DCkgatv1GwIXeefHthpvlkg4ybBlMnmxss,1739
|
167
169
|
clarifai/schema/search.py,sha256=JjTi8ammJgZZ2OGl4K6tIA4zEJ1Fr2ASZARXavI1j5c,2448
|
168
170
|
clarifai/schema/__pycache__/search.cpython-310.pyc,sha256=aYuMHmn0ovwmeOhTDj7QAURrQAjlyLm1CwKaz6xktZU,2484
|
@@ -193,9 +195,9 @@ clarifai/workflows/__pycache__/__init__.cpython-310.pyc,sha256=oRKg6B7Z-wWQy0EW2
|
|
193
195
|
clarifai/workflows/__pycache__/export.cpython-310.pyc,sha256=cNmGLnww7xVpm4htd1vRhQJoEZ1dhpN1oD8iLLAtVzM,2418
|
194
196
|
clarifai/workflows/__pycache__/utils.cpython-310.pyc,sha256=rm2kWk4a3GOKWoerXpEAEeRvGhEe7wPd0ZZ6jHtEGqY,1925
|
195
197
|
clarifai/workflows/__pycache__/validate.cpython-310.pyc,sha256=QA1i6YdDpY824cqtQvkEaFPpaCa2iqfOwFouqwZfAKY,2139
|
196
|
-
clarifai-11.1.
|
197
|
-
clarifai-11.1.
|
198
|
-
clarifai-11.1.
|
199
|
-
clarifai-11.1.
|
200
|
-
clarifai-11.1.
|
201
|
-
clarifai-11.1.
|
198
|
+
clarifai-11.1.5rc3.dist-info/LICENSE,sha256=mUqF_d12-qE2n41g7C5_sq-BMLOcj6CNN-jevr15YHU,555
|
199
|
+
clarifai-11.1.5rc3.dist-info/METADATA,sha256=Zl3cY8_BDZ33bV5M1VWnPiAMUGT4EIwE3ZqFHWaW0EY,22229
|
200
|
+
clarifai-11.1.5rc3.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
201
|
+
clarifai-11.1.5rc3.dist-info/entry_points.txt,sha256=X9FZ4Z-i_r2Ud1RpZ9sNIFYuu_-9fogzCMCRUD9hyX0,51
|
202
|
+
clarifai-11.1.5rc3.dist-info/top_level.txt,sha256=wUMdCQGjkxaynZ6nZ9FAnvBUCgp5RJUVFSy2j-KYo0s,9
|
203
|
+
clarifai-11.1.5rc3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|