clarifai 11.2.3rc1__py3-none-any.whl → 11.2.3rc2__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.
Files changed (248) hide show
  1. clarifai/__init__.py +1 -1
  2. clarifai/__pycache__/__init__.cpython-310.pyc +0 -0
  3. clarifai/__pycache__/__init__.cpython-39.pyc +0 -0
  4. clarifai/__pycache__/errors.cpython-310.pyc +0 -0
  5. clarifai/__pycache__/versions.cpython-310.pyc +0 -0
  6. clarifai/cli/__pycache__/__init__.cpython-310.pyc +0 -0
  7. clarifai/cli/__pycache__/base.cpython-310.pyc +0 -0
  8. clarifai/cli/__pycache__/base_cli.cpython-310.pyc +0 -0
  9. clarifai/cli/__pycache__/compute_cluster.cpython-310.pyc +0 -0
  10. clarifai/cli/__pycache__/deployment.cpython-310.pyc +0 -0
  11. clarifai/cli/__pycache__/model.cpython-310.pyc +0 -0
  12. clarifai/cli/__pycache__/model_cli.cpython-310.pyc +0 -0
  13. clarifai/cli/__pycache__/nodepool.cpython-310.pyc +0 -0
  14. clarifai/cli/base.py +81 -228
  15. clarifai/cli/compute_cluster.py +17 -25
  16. clarifai/cli/deployment.py +41 -67
  17. clarifai/cli/model.py +39 -26
  18. clarifai/cli/nodepool.py +40 -59
  19. clarifai/client/__pycache__/__init__.cpython-310.pyc +0 -0
  20. clarifai/client/__pycache__/__init__.cpython-39.pyc +0 -0
  21. clarifai/client/__pycache__/app.cpython-310.pyc +0 -0
  22. clarifai/client/__pycache__/app.cpython-39.pyc +0 -0
  23. clarifai/client/__pycache__/base.cpython-310.pyc +0 -0
  24. clarifai/client/__pycache__/compute_cluster.cpython-310.pyc +0 -0
  25. clarifai/client/__pycache__/dataset.cpython-310.pyc +0 -0
  26. clarifai/client/__pycache__/deployment.cpython-310.pyc +0 -0
  27. clarifai/client/__pycache__/input.cpython-310.pyc +0 -0
  28. clarifai/client/__pycache__/lister.cpython-310.pyc +0 -0
  29. clarifai/client/__pycache__/model.cpython-310.pyc +0 -0
  30. clarifai/client/__pycache__/module.cpython-310.pyc +0 -0
  31. clarifai/client/__pycache__/nodepool.cpython-310.pyc +0 -0
  32. clarifai/client/__pycache__/search.cpython-310.pyc +0 -0
  33. clarifai/client/__pycache__/user.cpython-310.pyc +0 -0
  34. clarifai/client/__pycache__/workflow.cpython-310.pyc +0 -0
  35. clarifai/client/app.py +1 -1
  36. clarifai/client/auth/__pycache__/__init__.cpython-310.pyc +0 -0
  37. clarifai/client/auth/__pycache__/helper.cpython-310.pyc +0 -0
  38. clarifai/client/auth/__pycache__/register.cpython-310.pyc +0 -0
  39. clarifai/client/auth/__pycache__/stub.cpython-310.pyc +0 -0
  40. clarifai/client/cli/__init__.py +0 -0
  41. clarifai/client/cli/__pycache__/__init__.cpython-310.pyc +0 -0
  42. clarifai/client/cli/__pycache__/base_cli.cpython-310.pyc +0 -0
  43. clarifai/client/cli/__pycache__/model_cli.cpython-310.pyc +0 -0
  44. clarifai/client/cli/base_cli.py +88 -0
  45. clarifai/client/cli/model_cli.py +29 -0
  46. clarifai/client/model.py +159 -393
  47. clarifai/client/model_client.py +502 -0
  48. clarifai/constants/__pycache__/base.cpython-310.pyc +0 -0
  49. clarifai/constants/__pycache__/dataset.cpython-310.pyc +0 -0
  50. clarifai/constants/__pycache__/input.cpython-310.pyc +0 -0
  51. clarifai/constants/__pycache__/{model.cpython-312.pyc → model.cpython-310.pyc} +0 -0
  52. clarifai/constants/__pycache__/rag.cpython-310.pyc +0 -0
  53. clarifai/constants/__pycache__/search.cpython-310.pyc +0 -0
  54. clarifai/constants/__pycache__/workflow.cpython-310.pyc +0 -0
  55. clarifai/datasets/__pycache__/__init__.cpython-310.pyc +0 -0
  56. clarifai/datasets/__pycache__/__init__.cpython-39.pyc +0 -0
  57. clarifai/datasets/export/__pycache__/__init__.cpython-310.pyc +0 -0
  58. clarifai/datasets/export/__pycache__/__init__.cpython-39.pyc +0 -0
  59. clarifai/datasets/export/__pycache__/inputs_annotations.cpython-310.pyc +0 -0
  60. clarifai/datasets/upload/__pycache__/__init__.cpython-310.pyc +0 -0
  61. clarifai/datasets/upload/__pycache__/__init__.cpython-39.pyc +0 -0
  62. clarifai/datasets/upload/__pycache__/base.cpython-310.pyc +0 -0
  63. clarifai/datasets/upload/__pycache__/features.cpython-310.pyc +0 -0
  64. clarifai/datasets/upload/__pycache__/image.cpython-310.pyc +0 -0
  65. clarifai/datasets/upload/__pycache__/multimodal.cpython-310.pyc +0 -0
  66. clarifai/datasets/upload/__pycache__/text.cpython-310.pyc +0 -0
  67. clarifai/datasets/upload/__pycache__/utils.cpython-310.pyc +0 -0
  68. clarifai/datasets/upload/loaders/__pycache__/__init__.cpython-39.pyc +0 -0
  69. clarifai/models/__pycache__/__init__.cpython-39.pyc +0 -0
  70. clarifai/modules/__pycache__/__init__.cpython-39.pyc +0 -0
  71. clarifai/rag/__pycache__/__init__.cpython-310.pyc +0 -0
  72. clarifai/rag/__pycache__/__init__.cpython-39.pyc +0 -0
  73. clarifai/rag/__pycache__/rag.cpython-310.pyc +0 -0
  74. clarifai/rag/__pycache__/rag.cpython-39.pyc +0 -0
  75. clarifai/rag/__pycache__/utils.cpython-310.pyc +0 -0
  76. clarifai/runners/__init__.py +2 -7
  77. clarifai/runners/__pycache__/__init__.cpython-310.pyc +0 -0
  78. clarifai/runners/__pycache__/__init__.cpython-39.pyc +0 -0
  79. clarifai/runners/dockerfile_template/Dockerfile.cpu.template +31 -0
  80. clarifai/runners/dockerfile_template/Dockerfile.cuda.template +42 -0
  81. clarifai/runners/dockerfile_template/Dockerfile.nim +71 -0
  82. clarifai/runners/models/__pycache__/__init__.cpython-310.pyc +0 -0
  83. clarifai/runners/models/__pycache__/__init__.cpython-39.pyc +0 -0
  84. clarifai/runners/models/__pycache__/base_typed_model.cpython-310.pyc +0 -0
  85. clarifai/runners/models/__pycache__/base_typed_model.cpython-39.pyc +0 -0
  86. clarifai/runners/models/__pycache__/model_class.cpython-310.pyc +0 -0
  87. clarifai/runners/models/__pycache__/model_run_locally.cpython-310-pytest-7.1.2.pyc +0 -0
  88. clarifai/runners/models/__pycache__/model_run_locally.cpython-310.pyc +0 -0
  89. clarifai/runners/models/__pycache__/model_runner.cpython-310.pyc +0 -0
  90. clarifai/runners/models/__pycache__/model_upload.cpython-310.pyc +0 -0
  91. clarifai/runners/models/model_builder.py +138 -51
  92. clarifai/runners/models/model_class.py +441 -28
  93. clarifai/runners/models/model_class_refract.py +80 -0
  94. clarifai/runners/models/model_run_locally.py +25 -89
  95. clarifai/runners/models/model_runner.py +8 -0
  96. clarifai/runners/models/model_servicer.py +11 -2
  97. clarifai/runners/models/model_upload.py +607 -0
  98. clarifai/runners/models/temp.py +25 -0
  99. clarifai/runners/utils/__pycache__/__init__.cpython-310.pyc +0 -0
  100. clarifai/runners/utils/__pycache__/__init__.cpython-38.pyc +0 -0
  101. clarifai/runners/utils/__pycache__/__init__.cpython-39.pyc +0 -0
  102. clarifai/runners/utils/__pycache__/buffered_stream.cpython-310.pyc +0 -0
  103. clarifai/runners/utils/__pycache__/buffered_stream.cpython-38.pyc +0 -0
  104. clarifai/runners/utils/__pycache__/buffered_stream.cpython-39.pyc +0 -0
  105. clarifai/runners/utils/__pycache__/const.cpython-310.pyc +0 -0
  106. clarifai/runners/utils/__pycache__/constants.cpython-310.pyc +0 -0
  107. clarifai/runners/utils/__pycache__/constants.cpython-38.pyc +0 -0
  108. clarifai/runners/utils/__pycache__/constants.cpython-39.pyc +0 -0
  109. clarifai/runners/utils/__pycache__/data_handler.cpython-310.pyc +0 -0
  110. clarifai/runners/utils/__pycache__/data_handler.cpython-38.pyc +0 -0
  111. clarifai/runners/utils/__pycache__/data_handler.cpython-39.pyc +0 -0
  112. clarifai/runners/utils/__pycache__/data_utils.cpython-310.pyc +0 -0
  113. clarifai/runners/utils/__pycache__/data_utils.cpython-38.pyc +0 -0
  114. clarifai/runners/utils/__pycache__/data_utils.cpython-39.pyc +0 -0
  115. clarifai/runners/utils/__pycache__/grpc_server.cpython-310.pyc +0 -0
  116. clarifai/runners/utils/__pycache__/grpc_server.cpython-38.pyc +0 -0
  117. clarifai/runners/utils/__pycache__/grpc_server.cpython-39.pyc +0 -0
  118. clarifai/runners/utils/__pycache__/health.cpython-310.pyc +0 -0
  119. clarifai/runners/utils/__pycache__/health.cpython-38.pyc +0 -0
  120. clarifai/runners/utils/__pycache__/health.cpython-39.pyc +0 -0
  121. clarifai/runners/utils/__pycache__/loader.cpython-310.pyc +0 -0
  122. clarifai/runners/utils/__pycache__/logging.cpython-310.pyc +0 -0
  123. clarifai/runners/utils/__pycache__/logging.cpython-38.pyc +0 -0
  124. clarifai/runners/utils/__pycache__/logging.cpython-39.pyc +0 -0
  125. clarifai/runners/utils/__pycache__/stream_source.cpython-310.pyc +0 -0
  126. clarifai/runners/utils/__pycache__/stream_source.cpython-39.pyc +0 -0
  127. clarifai/runners/utils/__pycache__/url_fetcher.cpython-310.pyc +0 -0
  128. clarifai/runners/utils/__pycache__/url_fetcher.cpython-38.pyc +0 -0
  129. clarifai/runners/utils/__pycache__/url_fetcher.cpython-39.pyc +0 -0
  130. clarifai/runners/utils/code_script.py +217 -0
  131. clarifai/runners/utils/const.py +8 -9
  132. clarifai/runners/utils/data_handler.py +271 -210
  133. clarifai/runners/utils/data_handler_refract.py +213 -0
  134. clarifai/runners/utils/data_types.py +473 -0
  135. clarifai/runners/utils/data_utils.py +165 -0
  136. clarifai/runners/utils/loader.py +6 -36
  137. clarifai/runners/utils/logger.py +0 -0
  138. clarifai/runners/utils/method_signatures.py +518 -0
  139. clarifai/runners/utils/serializers.py +222 -0
  140. clarifai/schema/__pycache__/search.cpython-310.pyc +0 -0
  141. clarifai/urls/__pycache__/helper.cpython-310.pyc +0 -0
  142. clarifai/utils/__pycache__/__init__.cpython-310.pyc +0 -0
  143. clarifai/utils/__pycache__/__init__.cpython-39.pyc +0 -0
  144. clarifai/utils/__pycache__/cli.cpython-310.pyc +0 -0
  145. clarifai/utils/__pycache__/constants.cpython-310.pyc +0 -0
  146. clarifai/utils/__pycache__/logging.cpython-310.pyc +0 -0
  147. clarifai/utils/__pycache__/misc.cpython-310.pyc +0 -0
  148. clarifai/utils/__pycache__/model_train.cpython-310.pyc +0 -0
  149. clarifai/utils/cli.py +33 -132
  150. clarifai/utils/constants.py +0 -4
  151. clarifai/utils/evaluation/__pycache__/__init__.cpython-39.pyc +0 -0
  152. clarifai/utils/evaluation/__pycache__/main.cpython-39.pyc +0 -0
  153. clarifai/utils/misc.py +0 -2
  154. clarifai/workflows/__pycache__/__init__.cpython-310.pyc +0 -0
  155. clarifai/workflows/__pycache__/__init__.cpython-39.pyc +0 -0
  156. clarifai/workflows/__pycache__/export.cpython-310.pyc +0 -0
  157. clarifai/workflows/__pycache__/utils.cpython-310.pyc +0 -0
  158. clarifai/workflows/__pycache__/validate.cpython-310.pyc +0 -0
  159. {clarifai-11.2.3rc1.dist-info → clarifai-11.2.3rc2.dist-info}/METADATA +14 -3
  160. clarifai-11.2.3rc2.dist-info/RECORD +238 -0
  161. {clarifai-11.2.3rc1.dist-info → clarifai-11.2.3rc2.dist-info}/WHEEL +1 -1
  162. clarifai/__pycache__/__init__.cpython-312.pyc +0 -0
  163. clarifai/__pycache__/errors.cpython-312.pyc +0 -0
  164. clarifai/__pycache__/versions.cpython-312.pyc +0 -0
  165. clarifai/cli/__pycache__/__init__.cpython-312.pyc +0 -0
  166. clarifai/cli/__pycache__/base.cpython-312.pyc +0 -0
  167. clarifai/cli/__pycache__/compute_cluster.cpython-312.pyc +0 -0
  168. clarifai/cli/__pycache__/deployment.cpython-312.pyc +0 -0
  169. clarifai/cli/__pycache__/model.cpython-312.pyc +0 -0
  170. clarifai/cli/__pycache__/nodepool.cpython-312.pyc +0 -0
  171. clarifai/client/__pycache__/__init__.cpython-312.pyc +0 -0
  172. clarifai/client/__pycache__/app.cpython-312.pyc +0 -0
  173. clarifai/client/__pycache__/base.cpython-312.pyc +0 -0
  174. clarifai/client/__pycache__/compute_cluster.cpython-312.pyc +0 -0
  175. clarifai/client/__pycache__/dataset.cpython-312.pyc +0 -0
  176. clarifai/client/__pycache__/deployment.cpython-312.pyc +0 -0
  177. clarifai/client/__pycache__/input.cpython-312.pyc +0 -0
  178. clarifai/client/__pycache__/lister.cpython-312.pyc +0 -0
  179. clarifai/client/__pycache__/model.cpython-312.pyc +0 -0
  180. clarifai/client/__pycache__/model_client.cpython-312.pyc +0 -0
  181. clarifai/client/__pycache__/module.cpython-312.pyc +0 -0
  182. clarifai/client/__pycache__/nodepool.cpython-312.pyc +0 -0
  183. clarifai/client/__pycache__/search.cpython-312.pyc +0 -0
  184. clarifai/client/__pycache__/user.cpython-312.pyc +0 -0
  185. clarifai/client/__pycache__/workflow.cpython-312.pyc +0 -0
  186. clarifai/client/auth/__pycache__/__init__.cpython-312.pyc +0 -0
  187. clarifai/client/auth/__pycache__/helper.cpython-312.pyc +0 -0
  188. clarifai/client/auth/__pycache__/register.cpython-312.pyc +0 -0
  189. clarifai/client/auth/__pycache__/stub.cpython-312.pyc +0 -0
  190. clarifai/constants/__pycache__/base.cpython-312.pyc +0 -0
  191. clarifai/constants/__pycache__/dataset.cpython-312.pyc +0 -0
  192. clarifai/constants/__pycache__/input.cpython-312.pyc +0 -0
  193. clarifai/constants/__pycache__/search.cpython-312.pyc +0 -0
  194. clarifai/constants/__pycache__/workflow.cpython-312.pyc +0 -0
  195. clarifai/datasets/__pycache__/__init__.cpython-312.pyc +0 -0
  196. clarifai/datasets/export/__pycache__/__init__.cpython-312.pyc +0 -0
  197. clarifai/datasets/export/__pycache__/inputs_annotations.cpython-312.pyc +0 -0
  198. clarifai/datasets/upload/__pycache__/__init__.cpython-312.pyc +0 -0
  199. clarifai/datasets/upload/__pycache__/base.cpython-312.pyc +0 -0
  200. clarifai/datasets/upload/__pycache__/features.cpython-312.pyc +0 -0
  201. clarifai/datasets/upload/__pycache__/image.cpython-312.pyc +0 -0
  202. clarifai/datasets/upload/__pycache__/multimodal.cpython-312.pyc +0 -0
  203. clarifai/datasets/upload/__pycache__/text.cpython-312.pyc +0 -0
  204. clarifai/datasets/upload/__pycache__/utils.cpython-312.pyc +0 -0
  205. clarifai/datasets/upload/loaders/__pycache__/__init__.cpython-312.pyc +0 -0
  206. clarifai/datasets/upload/loaders/__pycache__/coco_detection.cpython-312.pyc +0 -0
  207. clarifai/modules/__pycache__/__init__.cpython-312.pyc +0 -0
  208. clarifai/modules/__pycache__/css.cpython-312.pyc +0 -0
  209. clarifai/runners/__pycache__/__init__.cpython-312.pyc +0 -0
  210. clarifai/runners/__pycache__/server.cpython-312.pyc +0 -0
  211. clarifai/runners/models/__pycache__/__init__.cpython-312.pyc +0 -0
  212. clarifai/runners/models/__pycache__/base_typed_model.cpython-312.pyc +0 -0
  213. clarifai/runners/models/__pycache__/model_builder.cpython-312.pyc +0 -0
  214. clarifai/runners/models/__pycache__/model_class.cpython-312.pyc +0 -0
  215. clarifai/runners/models/__pycache__/model_run_locally.cpython-312.pyc +0 -0
  216. clarifai/runners/models/__pycache__/model_runner.cpython-312.pyc +0 -0
  217. clarifai/runners/models/__pycache__/model_servicer.cpython-312.pyc +0 -0
  218. clarifai/runners/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  219. clarifai/runners/utils/__pycache__/const.cpython-312.pyc +0 -0
  220. clarifai/runners/utils/__pycache__/data_handler.cpython-312.pyc +0 -0
  221. clarifai/runners/utils/__pycache__/data_types.cpython-312.pyc +0 -0
  222. clarifai/runners/utils/__pycache__/data_utils.cpython-312.pyc +0 -0
  223. clarifai/runners/utils/__pycache__/loader.cpython-312.pyc +0 -0
  224. clarifai/runners/utils/__pycache__/method_signatures.cpython-312.pyc +0 -0
  225. clarifai/runners/utils/__pycache__/serializers.cpython-312.pyc +0 -0
  226. clarifai/runners/utils/__pycache__/url_fetcher.cpython-312.pyc +0 -0
  227. clarifai/schema/__pycache__/search.cpython-312.pyc +0 -0
  228. clarifai/urls/__pycache__/helper.cpython-312.pyc +0 -0
  229. clarifai/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  230. clarifai/utils/__pycache__/cli.cpython-312.pyc +0 -0
  231. clarifai/utils/__pycache__/config.cpython-312.pyc +0 -0
  232. clarifai/utils/__pycache__/constants.cpython-312.pyc +0 -0
  233. clarifai/utils/__pycache__/logging.cpython-312.pyc +0 -0
  234. clarifai/utils/__pycache__/misc.cpython-312.pyc +0 -0
  235. clarifai/utils/__pycache__/model_train.cpython-312.pyc +0 -0
  236. clarifai/utils/config.py +0 -105
  237. clarifai/utils/config.py~ +0 -145
  238. clarifai/utils/evaluation/__pycache__/__init__.cpython-312.pyc +0 -0
  239. clarifai/utils/evaluation/__pycache__/helpers.cpython-312.pyc +0 -0
  240. clarifai/utils/evaluation/__pycache__/main.cpython-312.pyc +0 -0
  241. clarifai/workflows/__pycache__/__init__.cpython-312.pyc +0 -0
  242. clarifai/workflows/__pycache__/export.cpython-312.pyc +0 -0
  243. clarifai/workflows/__pycache__/utils.cpython-312.pyc +0 -0
  244. clarifai/workflows/__pycache__/validate.cpython-312.pyc +0 -0
  245. clarifai-11.2.3rc1.dist-info/RECORD +0 -185
  246. {clarifai-11.2.3rc1.dist-info → clarifai-11.2.3rc2.dist-info}/LICENSE +0 -0
  247. {clarifai-11.2.3rc1.dist-info → clarifai-11.2.3rc2.dist-info}/entry_points.txt +0 -0
  248. {clarifai-11.2.3rc1.dist-info → clarifai-11.2.3rc2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,518 @@
1
+ import inspect
2
+ import json
3
+ from collections import namedtuple
4
+ from typing import Dict, List, Tuple, get_args, get_origin
5
+
6
+ import numpy as np
7
+ import PIL.Image
8
+ import yaml
9
+ from clarifai_grpc.grpc.api import resources_pb2
10
+ from google.protobuf.json_format import MessageToDict, ParseDict
11
+ from google.protobuf.message import Message as MessageProto
12
+
13
+ from clarifai.runners.utils import data_types, data_utils
14
+ from clarifai.runners.utils.code_script import _get_base_type, _parse_default_value
15
+ from clarifai.runners.utils.serializers import (
16
+ AtomicFieldSerializer, JSONSerializer, ListSerializer, MessageSerializer,
17
+ NamedFieldsSerializer, NDArraySerializer, Serializer, TupleSerializer)
18
+
19
+
20
+ def build_function_signature(func):
21
+ '''
22
+ Build a signature for the given function.
23
+ '''
24
+ sig = inspect.signature(func)
25
+
26
+ # check if func is bound, and if not, remove self/cls
27
+ if getattr(func, '__self__', None) is None and sig.parameters and list(
28
+ sig.parameters.values())[0].name in ('self', 'cls'):
29
+ sig = sig.replace(parameters=list(sig.parameters.values())[1:])
30
+
31
+ return_annotation = sig.return_annotation
32
+ if return_annotation == inspect.Parameter.empty:
33
+ raise TypeError('Function must have a return annotation')
34
+
35
+ input_sigs = []
36
+ input_streaming = []
37
+ for p in sig.parameters.values():
38
+ model_type_field, _, streaming = build_variable_signature(p.name, p.annotation, p.default)
39
+ input_sigs.append(model_type_field)
40
+ input_streaming.append(streaming)
41
+
42
+ output_sig, output_type, output_streaming = build_variable_signature(
43
+ 'return', return_annotation, is_output=True)
44
+ # TODO: flatten out "return" layer if not needed
45
+
46
+ # check for streams and determine method type
47
+ if sum(input_streaming) > 1:
48
+ raise TypeError('streaming methods must have at most one streaming input')
49
+ input_streaming = any(input_streaming)
50
+ if not (input_streaming or output_streaming):
51
+ method_type = 'UNARY_UNARY'
52
+ elif not input_streaming and output_streaming:
53
+ method_type = 'UNARY_STREAMING'
54
+ elif input_streaming and output_streaming:
55
+ method_type = 'STREAMING_STREAMING'
56
+ else:
57
+ raise TypeError('stream methods with streaming inputs must have streaming outputs')
58
+
59
+ method_signature = resources_pb2.MethodSignature()
60
+
61
+ method_signature.name = func.__name__
62
+ method_signature.method_type = getattr(resources_pb2.RunnerMethodType, method_type)
63
+ assert method_type in ('UNARY_UNARY', 'UNARY_STREAMING', 'STREAMING_STREAMING')
64
+ # method_signature.method_type = method_type
65
+ method_signature.description = inspect.cleandoc(func.__doc__ or '')
66
+ # method_signature.annotations_json = json.dumps(_get_annotations_source(func))
67
+
68
+ method_signature.input_fields.extend(input_sigs)
69
+ method_signature.output_fields.append(output_sig)
70
+ return method_signature
71
+
72
+
73
+ # def _get_annotations_source(func):
74
+ # """Extracts raw annotation strings from the function source."""
75
+ # source = inspect.getsource(func) # Get function source code
76
+ # source = textwrap.dedent(source) # Dedent source code
77
+ # tree = ast.parse(source) # Parse into AST
78
+ # func_node = next(node for node in tree.body
79
+ # if isinstance(node, ast.FunctionDef)) # Get function node
80
+
81
+ # annotations = {}
82
+ # for arg in func_node.args.args: # Process arguments
83
+ # if arg.annotation:
84
+ # annotations[arg.arg] = ast.unparse(arg.annotation) # Get raw annotation string
85
+
86
+ # if func_node.returns: # Process return type
87
+ # annotations["return"] = ast.unparse(func_node.returns)
88
+
89
+ # return annotations
90
+
91
+
92
+ def _process_input_field(field: resources_pb2.ModelTypeField) -> str:
93
+ base_type = _get_base_type(field)
94
+ if field.iterator:
95
+ type_str = f"Iterator[{base_type}]"
96
+ else:
97
+ type_str = base_type
98
+ default = _parse_default_value(field)
99
+ param = f"{field.name}: {type_str}"
100
+ if default is not None:
101
+ param += f" = {default}"
102
+ return param
103
+
104
+
105
+ def _process_output_field(field: resources_pb2.ModelTypeField) -> str:
106
+ base_type = _get_base_type(field)
107
+ if field.iterator:
108
+ return f"Iterator[{base_type}]"
109
+ else:
110
+ return base_type
111
+
112
+
113
+ def get_method_signature(method_signature: resources_pb2.MethodSignature) -> str:
114
+ """
115
+ Get the method signature of a method in a model.
116
+ """
117
+ # Process input fields
118
+ input_params = []
119
+ for input_field in method_signature.input_fields:
120
+ param_str = _process_input_field(input_field)
121
+ input_params.append(param_str)
122
+
123
+ # Process output field
124
+ if not method_signature.output_fields:
125
+ raise ValueError("MethodSignature must have at least one output field")
126
+ output_field = method_signature.output_fields[0]
127
+ return_type = _process_output_field(output_field)
128
+
129
+ # Generate function signature
130
+ function_def = f"def {method_signature.name}({', '.join(input_params)}) -> {return_type}:"
131
+ return function_def
132
+
133
+
134
+ def build_variable_signature(name, annotation, default=inspect.Parameter.empty, is_output=False):
135
+ '''
136
+ Build a data proto signature and get the normalized python type for the given annotation.
137
+ '''
138
+
139
+ # check valid names (should already be constrained by python naming, but check anyway)
140
+ if not name.isidentifier():
141
+ raise ValueError(f'Invalid variable name: {name}')
142
+
143
+ # get fields for each variable based on type
144
+ tp, streaming = _normalize_type(annotation)
145
+
146
+ sig = resources_pb2.ModelTypeField()
147
+ sig.name = name
148
+ sig.iterator = streaming
149
+
150
+ if not is_output:
151
+ sig.required = (default is inspect.Parameter.empty)
152
+ if not sig.required:
153
+ if isinstance(default, data_utils.InputField):
154
+ sig = default.to_proto(sig)
155
+ else:
156
+ sig = data_utils.InputField.set_default(sig, default)
157
+
158
+ _fill_signature_type(sig, tp)
159
+
160
+ return sig, type, streaming
161
+
162
+
163
+ def _fill_signature_type(sig, tp):
164
+ try:
165
+ if tp in _DATA_TYPES:
166
+ sig.type = _DATA_TYPES[tp].type
167
+ return
168
+ except TypeError:
169
+ pass # not hashable type
170
+
171
+ # Handle NamedFields with annotations
172
+ # Check for dynamically generated NamedFields subclasses (from type annotations)
173
+ if inspect.isclass(tp) and issubclass(tp, data_types.NamedFields) and hasattr(
174
+ tp, '__annotations__'):
175
+ sig.type = resources_pb2.ModelTypeField.DataType.NAMED_FIELDS
176
+ for name, inner_type in tp.__annotations__.items():
177
+ inner_sig = sig.type_args.add()
178
+ inner_sig.name = name
179
+ _fill_signature_type(inner_sig, inner_type)
180
+ return
181
+
182
+ # Handle NamedFields instances (dict-like)
183
+ if isinstance(tp, data_types.NamedFields):
184
+ sig.type = resources_pb2.ModelTypeField.DataType.NAMED_FIELDS
185
+ for name, inner_type in tp.items():
186
+ inner_sig = sig.type_args.add()
187
+ inner_sig.name = name
188
+ _fill_signature_type(inner_sig, inner_type)
189
+ return
190
+
191
+ origin = get_origin(tp)
192
+ args = get_args(tp)
193
+
194
+ # Handle Tuple type
195
+ if origin == tuple:
196
+ sig.type = resources_pb2.ModelTypeField.DataType.TUPLE
197
+ for inner_type in args:
198
+ inner_sig = sig.type_args.add()
199
+ inner_sig.name = sig.name + '_item'
200
+ _fill_signature_type(inner_sig, inner_type)
201
+ return
202
+
203
+ # Handle List type
204
+ if origin == list:
205
+ sig.type = resources_pb2.ModelTypeField.DataType.LIST
206
+ inner_sig = sig.type_args.add()
207
+ inner_sig.name = sig.name + '_item'
208
+ _fill_signature_type(inner_sig, args[0])
209
+ return
210
+
211
+ raise TypeError(f'Unsupported type: {tp}')
212
+
213
+
214
+ def serializer_from_signature(signature):
215
+ '''
216
+ Get the serializer for the given signature.
217
+ '''
218
+ if signature.type in _SERIALIZERS_BY_TYPE_ENUM:
219
+ return _SERIALIZERS_BY_TYPE_ENUM[signature.type]
220
+ if signature.type == resources_pb2.ModelTypeField.DataType.LIST:
221
+ return ListSerializer(serializer_from_signature(signature.type_args[0]))
222
+ if signature.type == resources_pb2.ModelTypeField.DataType.TUPLE:
223
+ return TupleSerializer([serializer_from_signature(sig) for sig in signature.type_args])
224
+ if signature.type == resources_pb2.ModelTypeField.DataType.NAMED_FIELDS:
225
+ return NamedFieldsSerializer(
226
+ {sig.name: serializer_from_signature(sig)
227
+ for sig in signature.type_args})
228
+ raise ValueError(f'Unsupported type: {signature.type}')
229
+
230
+
231
+ def signatures_to_json(signatures):
232
+ assert isinstance(
233
+ signatures, dict), 'Expected dict of signatures {name: signature}, got %s' % type(signatures)
234
+ # TODO change to proto when ready
235
+ signatures = {name: MessageToDict(sig) for name, sig in signatures.items()}
236
+ return json.dumps(signatures)
237
+
238
+
239
+ def signatures_from_json(json_str):
240
+ signatures_dict = json.loads(json_str)
241
+ assert isinstance(signatures_dict, dict), "Expected JSON to decode into a dictionary"
242
+
243
+ return {
244
+ name: ParseDict(sig_dict, resources_pb2.MethodSignature())
245
+ for name, sig_dict in signatures_dict.items()
246
+ }
247
+ # d = json.loads(json_str, object_pairs_hook=_SignatureDict)
248
+ # return d
249
+
250
+
251
+ def signatures_to_yaml(signatures):
252
+ # XXX go in/out of json to get the correct format and python dict types
253
+ d = json.loads(signatures_to_json(signatures))
254
+
255
+ def _filter_empty(d):
256
+ if isinstance(d, (list, tuple)):
257
+ return [_filter_empty(v) for v in d if v]
258
+ if isinstance(d, dict):
259
+ return {k: _filter_empty(v) for k, v in d.items() if v}
260
+ return d
261
+
262
+ return yaml.dump(_filter_empty(d), default_flow_style=False)
263
+
264
+
265
+ def signatures_from_yaml(yaml_str):
266
+ d = yaml.safe_load(yaml_str)
267
+ return signatures_from_json(json.dumps(d))
268
+
269
+
270
+ def serialize(kwargs, signatures, proto=None, is_output=False):
271
+ '''
272
+ Serialize the given kwargs into the proto using the given signatures.
273
+ '''
274
+ if proto is None:
275
+ proto = resources_pb2.Data()
276
+ unknown = set(kwargs.keys()) - set(sig.name for sig in signatures)
277
+ if unknown:
278
+ if unknown == {'return'} and len(signatures) > 1:
279
+ raise TypeError('Got a single return value, but expected multiple outputs {%s}' %
280
+ ', '.join(sig.name for sig in signatures))
281
+ raise TypeError('Got unexpected key: %s' % ', '.join(unknown))
282
+ inline_first_value = False
283
+ if (is_output and len(signatures) == 1 and signatures[0].name == 'return' and
284
+ len(kwargs) == 1 and 'return' in kwargs):
285
+ # if there is only one output, flatten it and return directly
286
+ inline_first_value = True
287
+ for sig_i, sig in enumerate(signatures):
288
+ if sig.name not in kwargs:
289
+ if sig.required:
290
+ raise TypeError(f'Missing required argument: {sig.name}')
291
+ continue # skip missing fields, they can be set to default on the server
292
+ data = kwargs[sig.name]
293
+ serializer = serializer_from_signature(sig)
294
+ # TODO determine if any (esp the first) var can go in the proto without parts
295
+ # and whether to put this in the signature or dynamically determine it
296
+ if inline_first_value and sig_i == 0 and id(data) not in _ZERO_VALUE_IDS:
297
+ # inlined first value; note data must not be empty or 0 to inline, since that
298
+ # will correspond to the missing value case (which uses function defaults).
299
+ # empty values are put explicitly in parts.
300
+ serializer.serialize(proto, data)
301
+ else:
302
+ # add the part to the proto
303
+ part = proto.parts.add()
304
+ part.id = sig.name
305
+ serializer.serialize(part.data, data)
306
+ return proto
307
+
308
+
309
+ def deserialize(proto, signatures, inference_params={}, is_output=False):
310
+ '''
311
+ Deserialize the given proto into kwargs using the given signatures.
312
+ '''
313
+ if isinstance(signatures, dict):
314
+ signatures = [signatures] # TODO update return key level and make consistnet
315
+ kwargs = {}
316
+ parts_by_name = {part.id: part for part in proto.parts}
317
+ for sig_i, sig in enumerate(signatures):
318
+ serializer = serializer_from_signature(sig)
319
+ part = parts_by_name.get(sig.name)
320
+ inference_params_value = inference_params.get(sig.name)
321
+ if part is not None:
322
+ kwargs[sig.name] = serializer.deserialize(part.data)
323
+ elif inference_params_value is not None:
324
+ kwargs[sig.name] = inference_params_value
325
+ else:
326
+ if sig_i == 0:
327
+ # possible inlined first value
328
+ value = serializer.deserialize(proto)
329
+ if id(value) not in _ZERO_VALUE_IDS:
330
+ # note missing values are not set to defaults, since they are not in parts
331
+ # an actual zero value passed in must be set in an explicit part
332
+ kwargs[sig.name] = value
333
+ continue
334
+
335
+ if sig.required or is_output: # TODO allow optional outputs?
336
+ raise ValueError(f'Missing required field: {sig.name}')
337
+ continue
338
+ if len(kwargs) == 1 and 'return' in kwargs:
339
+ return kwargs['return']
340
+ return kwargs
341
+
342
+
343
+ def get_stream_from_signature(signatures):
344
+ '''
345
+ Get the stream signature from the given signatures.
346
+ '''
347
+ for sig in signatures:
348
+ if sig.iterator:
349
+ return sig
350
+ return None
351
+
352
+
353
+ def _is_empty_proto_data(data):
354
+ if isinstance(data, np.ndarray):
355
+ return False
356
+ if isinstance(data, MessageProto):
357
+ return not data.ByteSize()
358
+ return not data
359
+
360
+
361
+ def _normalize_type(tp):
362
+ '''
363
+ Normalize the types for the given parameter.
364
+ Returns the normalized type and whether the parameter is streaming.
365
+ '''
366
+ # stream type indicates streaming, not part of the data itself
367
+ # it can only be used at the top-level of the var type
368
+ streaming = (get_origin(tp) == data_types.Stream)
369
+ if streaming:
370
+ tp = get_args(tp)[0]
371
+
372
+ return _normalize_data_type(tp), streaming
373
+
374
+
375
+ def _normalize_data_type(tp):
376
+ # container types that need to be serialized as parts
377
+ if get_origin(tp) == list and get_args(tp):
378
+ return List[_normalize_data_type(get_args(tp)[0])]
379
+
380
+ if get_origin(tp) == tuple:
381
+ if not get_args(tp):
382
+ raise TypeError('Tuple must have types specified')
383
+ return Tuple[tuple(_normalize_data_type(val) for val in get_args(tp))]
384
+
385
+ if isinstance(tp, (tuple, list)):
386
+ return Tuple[tuple(_normalize_data_type(val) for val in tp)]
387
+
388
+ if tp == data_types.NamedFields:
389
+ raise TypeError('NamedFields must have types specified')
390
+
391
+ # Handle dynamically generated NamedFields subclasses with annotations
392
+ if isinstance(tp, type) and issubclass(tp, data_types.NamedFields) and hasattr(
393
+ tp, '__annotations__'):
394
+ return data_types.NamedFields(
395
+ **{k: _normalize_data_type(v)
396
+ for k, v in tp.__annotations__.items()})
397
+
398
+ if isinstance(tp, (dict, data_types.NamedFields)):
399
+ return data_types.NamedFields(**{name: _normalize_data_type(val) for name, val in tp.items()})
400
+
401
+ # check if numpy array type, and if so, use ndarray
402
+ if get_origin(tp) == np.ndarray:
403
+ return np.ndarray
404
+
405
+ # check for PIL images (sometimes types use the module, sometimes the class)
406
+ # set these to use the Image data handler
407
+ if tp in (data_types.Image, PIL.Image.Image):
408
+ return data_types.Image
409
+
410
+ if tp == PIL.Image:
411
+ raise TypeError('Use PIL.Image.Image instead of PIL.Image module')
412
+
413
+ # jsonable list and dict, these can be serialized as json
414
+ # (tuple we want to keep as a tuple for args and returns, so don't include here)
415
+ if tp in (list, dict, Dict) or (get_origin(tp) in (list, dict, Dict) and _is_jsonable(tp)):
416
+ return data_types.JSON
417
+
418
+ # check for known data types
419
+ try:
420
+ if tp in _DATA_TYPES:
421
+ return tp
422
+ except TypeError:
423
+ pass # not hashable type
424
+
425
+ raise TypeError(f'Unsupported type: {tp}')
426
+
427
+
428
+ def _is_jsonable(tp):
429
+ if tp in (dict, list, tuple, str, int, float, bool, type(None)):
430
+ return True
431
+ if get_origin(tp) in (tuple, list, dict):
432
+ return all(_is_jsonable(val) for val in get_args(tp))
433
+ return False
434
+
435
+
436
+ # type: name of the data type
437
+ # data_field: name of the field in the data proto
438
+ # serializer: serializer for the data type
439
+ _DataType = namedtuple('_DataType', ('type', 'serializer'))
440
+
441
+ _ZERO_VALUE_IDS = {id(None), id(''), id(b''), id(0), id(0.0), id(False)}
442
+
443
+ # simple, non-container types that correspond directly to a data field
444
+ _DATA_TYPES = {
445
+ str:
446
+ _DataType(resources_pb2.ModelTypeField.DataType.STR,
447
+ AtomicFieldSerializer('string_value')),
448
+ bytes:
449
+ _DataType(resources_pb2.ModelTypeField.DataType.BYTES,
450
+ AtomicFieldSerializer('bytes_value')),
451
+ int:
452
+ _DataType(resources_pb2.ModelTypeField.DataType.INT, AtomicFieldSerializer('int_value')),
453
+ float:
454
+ _DataType(resources_pb2.ModelTypeField.DataType.FLOAT,
455
+ AtomicFieldSerializer('float_value')),
456
+ bool:
457
+ _DataType(resources_pb2.ModelTypeField.DataType.BOOL, AtomicFieldSerializer('bool_value')),
458
+ np.ndarray:
459
+ _DataType(resources_pb2.ModelTypeField.DataType.NDARRAY, NDArraySerializer('ndarray')),
460
+ data_types.JSON:
461
+ _DataType(resources_pb2.ModelTypeField.DataType.JSON_DATA, JSONSerializer('string_value')
462
+ ), # TODO change to json_value when new proto is ready
463
+ data_types.Text:
464
+ _DataType(resources_pb2.ModelTypeField.DataType.TEXT,
465
+ MessageSerializer('text', data_types.Text)),
466
+ data_types.Image:
467
+ _DataType(resources_pb2.ModelTypeField.DataType.IMAGE,
468
+ MessageSerializer('image', data_types.Image)),
469
+ data_types.Concept:
470
+ _DataType(resources_pb2.ModelTypeField.DataType.CONCEPT,
471
+ MessageSerializer('concepts', data_types.Concept)),
472
+ data_types.Region:
473
+ _DataType(resources_pb2.ModelTypeField.DataType.REGION,
474
+ MessageSerializer('regions', data_types.Region)),
475
+ data_types.Frame:
476
+ _DataType(resources_pb2.ModelTypeField.DataType.FRAME,
477
+ MessageSerializer('frames', data_types.Frame)),
478
+ data_types.Audio:
479
+ _DataType(resources_pb2.ModelTypeField.DataType.AUDIO,
480
+ MessageSerializer('audio', data_types.Audio)),
481
+ data_types.Video:
482
+ _DataType(resources_pb2.ModelTypeField.DataType.VIDEO,
483
+ MessageSerializer('video', data_types.Video)),
484
+ }
485
+
486
+ _SERIALIZERS_BY_TYPE_ENUM = {dt.type: dt.serializer for dt in _DATA_TYPES.values()}
487
+
488
+
489
+ class CompatibilitySerializer(Serializer):
490
+ '''
491
+ Serialization of basic value types, used for backwards compatibility
492
+ with older models that don't have type signatures.
493
+ '''
494
+
495
+ def serialize(self, data_proto, value):
496
+ tp = _normalize_data_type(type(value))
497
+
498
+ try:
499
+ serializer = _DATA_TYPES[tp].serializer
500
+ except (KeyError, TypeError):
501
+ raise TypeError(f'serializer currently only supports basic types, got {tp}')
502
+
503
+ serializer.serialize(data_proto, value)
504
+
505
+ def deserialize(self, data_proto):
506
+ fields = [k.name for k, _ in data_proto.ListFields()]
507
+ if 'parts' in fields:
508
+ raise ValueError('serializer does not support parts')
509
+ serializers = [
510
+ serializer for serializer in _SERIALIZERS_BY_TYPE_ENUM.values()
511
+ if serializer.field_name in fields
512
+ ]
513
+ if not serializers:
514
+ raise ValueError('Returned data not recognized')
515
+ if len(serializers) != 1:
516
+ raise ValueError('Only single output supported for serializer')
517
+ serializer = serializers[0]
518
+ return serializer.deserialize(data_proto)