pixeltable 0.2.26__py3-none-any.whl → 0.5.7__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 (245) hide show
  1. pixeltable/__init__.py +83 -19
  2. pixeltable/_query.py +1444 -0
  3. pixeltable/_version.py +1 -0
  4. pixeltable/catalog/__init__.py +7 -4
  5. pixeltable/catalog/catalog.py +2394 -119
  6. pixeltable/catalog/column.py +225 -104
  7. pixeltable/catalog/dir.py +38 -9
  8. pixeltable/catalog/globals.py +53 -34
  9. pixeltable/catalog/insertable_table.py +265 -115
  10. pixeltable/catalog/path.py +80 -17
  11. pixeltable/catalog/schema_object.py +28 -43
  12. pixeltable/catalog/table.py +1270 -677
  13. pixeltable/catalog/table_metadata.py +103 -0
  14. pixeltable/catalog/table_version.py +1270 -751
  15. pixeltable/catalog/table_version_handle.py +109 -0
  16. pixeltable/catalog/table_version_path.py +137 -42
  17. pixeltable/catalog/tbl_ops.py +53 -0
  18. pixeltable/catalog/update_status.py +191 -0
  19. pixeltable/catalog/view.py +251 -134
  20. pixeltable/config.py +215 -0
  21. pixeltable/env.py +736 -285
  22. pixeltable/exceptions.py +26 -2
  23. pixeltable/exec/__init__.py +7 -2
  24. pixeltable/exec/aggregation_node.py +39 -21
  25. pixeltable/exec/cache_prefetch_node.py +87 -109
  26. pixeltable/exec/cell_materialization_node.py +268 -0
  27. pixeltable/exec/cell_reconstruction_node.py +168 -0
  28. pixeltable/exec/component_iteration_node.py +25 -28
  29. pixeltable/exec/data_row_batch.py +11 -46
  30. pixeltable/exec/exec_context.py +26 -11
  31. pixeltable/exec/exec_node.py +35 -27
  32. pixeltable/exec/expr_eval/__init__.py +3 -0
  33. pixeltable/exec/expr_eval/evaluators.py +365 -0
  34. pixeltable/exec/expr_eval/expr_eval_node.py +413 -0
  35. pixeltable/exec/expr_eval/globals.py +200 -0
  36. pixeltable/exec/expr_eval/row_buffer.py +74 -0
  37. pixeltable/exec/expr_eval/schedulers.py +413 -0
  38. pixeltable/exec/globals.py +35 -0
  39. pixeltable/exec/in_memory_data_node.py +35 -27
  40. pixeltable/exec/object_store_save_node.py +293 -0
  41. pixeltable/exec/row_update_node.py +44 -29
  42. pixeltable/exec/sql_node.py +414 -115
  43. pixeltable/exprs/__init__.py +8 -5
  44. pixeltable/exprs/arithmetic_expr.py +79 -45
  45. pixeltable/exprs/array_slice.py +5 -5
  46. pixeltable/exprs/column_property_ref.py +40 -26
  47. pixeltable/exprs/column_ref.py +254 -61
  48. pixeltable/exprs/comparison.py +14 -9
  49. pixeltable/exprs/compound_predicate.py +9 -10
  50. pixeltable/exprs/data_row.py +213 -72
  51. pixeltable/exprs/expr.py +270 -104
  52. pixeltable/exprs/expr_dict.py +6 -5
  53. pixeltable/exprs/expr_set.py +20 -11
  54. pixeltable/exprs/function_call.py +383 -284
  55. pixeltable/exprs/globals.py +18 -5
  56. pixeltable/exprs/in_predicate.py +7 -7
  57. pixeltable/exprs/inline_expr.py +37 -37
  58. pixeltable/exprs/is_null.py +8 -4
  59. pixeltable/exprs/json_mapper.py +120 -54
  60. pixeltable/exprs/json_path.py +90 -60
  61. pixeltable/exprs/literal.py +61 -16
  62. pixeltable/exprs/method_ref.py +7 -6
  63. pixeltable/exprs/object_ref.py +19 -8
  64. pixeltable/exprs/row_builder.py +238 -75
  65. pixeltable/exprs/rowid_ref.py +53 -15
  66. pixeltable/exprs/similarity_expr.py +65 -50
  67. pixeltable/exprs/sql_element_cache.py +5 -5
  68. pixeltable/exprs/string_op.py +107 -0
  69. pixeltable/exprs/type_cast.py +25 -13
  70. pixeltable/exprs/variable.py +2 -2
  71. pixeltable/func/__init__.py +9 -5
  72. pixeltable/func/aggregate_function.py +197 -92
  73. pixeltable/func/callable_function.py +119 -35
  74. pixeltable/func/expr_template_function.py +101 -48
  75. pixeltable/func/function.py +375 -62
  76. pixeltable/func/function_registry.py +20 -19
  77. pixeltable/func/globals.py +6 -5
  78. pixeltable/func/mcp.py +74 -0
  79. pixeltable/func/query_template_function.py +151 -35
  80. pixeltable/func/signature.py +178 -49
  81. pixeltable/func/tools.py +164 -0
  82. pixeltable/func/udf.py +176 -53
  83. pixeltable/functions/__init__.py +44 -4
  84. pixeltable/functions/anthropic.py +226 -47
  85. pixeltable/functions/audio.py +148 -11
  86. pixeltable/functions/bedrock.py +137 -0
  87. pixeltable/functions/date.py +188 -0
  88. pixeltable/functions/deepseek.py +113 -0
  89. pixeltable/functions/document.py +81 -0
  90. pixeltable/functions/fal.py +76 -0
  91. pixeltable/functions/fireworks.py +72 -20
  92. pixeltable/functions/gemini.py +249 -0
  93. pixeltable/functions/globals.py +208 -53
  94. pixeltable/functions/groq.py +108 -0
  95. pixeltable/functions/huggingface.py +1088 -95
  96. pixeltable/functions/image.py +155 -84
  97. pixeltable/functions/json.py +8 -11
  98. pixeltable/functions/llama_cpp.py +31 -19
  99. pixeltable/functions/math.py +169 -0
  100. pixeltable/functions/mistralai.py +50 -75
  101. pixeltable/functions/net.py +70 -0
  102. pixeltable/functions/ollama.py +29 -36
  103. pixeltable/functions/openai.py +548 -160
  104. pixeltable/functions/openrouter.py +143 -0
  105. pixeltable/functions/replicate.py +15 -14
  106. pixeltable/functions/reve.py +250 -0
  107. pixeltable/functions/string.py +310 -85
  108. pixeltable/functions/timestamp.py +37 -19
  109. pixeltable/functions/together.py +77 -120
  110. pixeltable/functions/twelvelabs.py +188 -0
  111. pixeltable/functions/util.py +7 -2
  112. pixeltable/functions/uuid.py +30 -0
  113. pixeltable/functions/video.py +1528 -117
  114. pixeltable/functions/vision.py +26 -26
  115. pixeltable/functions/voyageai.py +289 -0
  116. pixeltable/functions/whisper.py +19 -10
  117. pixeltable/functions/whisperx.py +179 -0
  118. pixeltable/functions/yolox.py +112 -0
  119. pixeltable/globals.py +716 -236
  120. pixeltable/index/__init__.py +3 -1
  121. pixeltable/index/base.py +17 -21
  122. pixeltable/index/btree.py +32 -22
  123. pixeltable/index/embedding_index.py +155 -92
  124. pixeltable/io/__init__.py +12 -7
  125. pixeltable/io/datarows.py +140 -0
  126. pixeltable/io/external_store.py +83 -125
  127. pixeltable/io/fiftyone.py +24 -33
  128. pixeltable/io/globals.py +47 -182
  129. pixeltable/io/hf_datasets.py +96 -127
  130. pixeltable/io/label_studio.py +171 -156
  131. pixeltable/io/lancedb.py +3 -0
  132. pixeltable/io/pandas.py +136 -115
  133. pixeltable/io/parquet.py +40 -153
  134. pixeltable/io/table_data_conduit.py +702 -0
  135. pixeltable/io/utils.py +100 -0
  136. pixeltable/iterators/__init__.py +8 -4
  137. pixeltable/iterators/audio.py +207 -0
  138. pixeltable/iterators/base.py +9 -3
  139. pixeltable/iterators/document.py +144 -87
  140. pixeltable/iterators/image.py +17 -38
  141. pixeltable/iterators/string.py +15 -12
  142. pixeltable/iterators/video.py +523 -127
  143. pixeltable/metadata/__init__.py +33 -8
  144. pixeltable/metadata/converters/convert_10.py +2 -3
  145. pixeltable/metadata/converters/convert_13.py +2 -2
  146. pixeltable/metadata/converters/convert_15.py +15 -11
  147. pixeltable/metadata/converters/convert_16.py +4 -5
  148. pixeltable/metadata/converters/convert_17.py +4 -5
  149. pixeltable/metadata/converters/convert_18.py +4 -6
  150. pixeltable/metadata/converters/convert_19.py +6 -9
  151. pixeltable/metadata/converters/convert_20.py +3 -6
  152. pixeltable/metadata/converters/convert_21.py +6 -8
  153. pixeltable/metadata/converters/convert_22.py +3 -2
  154. pixeltable/metadata/converters/convert_23.py +33 -0
  155. pixeltable/metadata/converters/convert_24.py +55 -0
  156. pixeltable/metadata/converters/convert_25.py +19 -0
  157. pixeltable/metadata/converters/convert_26.py +23 -0
  158. pixeltable/metadata/converters/convert_27.py +29 -0
  159. pixeltable/metadata/converters/convert_28.py +13 -0
  160. pixeltable/metadata/converters/convert_29.py +110 -0
  161. pixeltable/metadata/converters/convert_30.py +63 -0
  162. pixeltable/metadata/converters/convert_31.py +11 -0
  163. pixeltable/metadata/converters/convert_32.py +15 -0
  164. pixeltable/metadata/converters/convert_33.py +17 -0
  165. pixeltable/metadata/converters/convert_34.py +21 -0
  166. pixeltable/metadata/converters/convert_35.py +9 -0
  167. pixeltable/metadata/converters/convert_36.py +38 -0
  168. pixeltable/metadata/converters/convert_37.py +15 -0
  169. pixeltable/metadata/converters/convert_38.py +39 -0
  170. pixeltable/metadata/converters/convert_39.py +124 -0
  171. pixeltable/metadata/converters/convert_40.py +73 -0
  172. pixeltable/metadata/converters/convert_41.py +12 -0
  173. pixeltable/metadata/converters/convert_42.py +9 -0
  174. pixeltable/metadata/converters/convert_43.py +44 -0
  175. pixeltable/metadata/converters/util.py +44 -18
  176. pixeltable/metadata/notes.py +21 -0
  177. pixeltable/metadata/schema.py +185 -42
  178. pixeltable/metadata/utils.py +74 -0
  179. pixeltable/mypy/__init__.py +3 -0
  180. pixeltable/mypy/mypy_plugin.py +123 -0
  181. pixeltable/plan.py +616 -225
  182. pixeltable/share/__init__.py +3 -0
  183. pixeltable/share/packager.py +797 -0
  184. pixeltable/share/protocol/__init__.py +33 -0
  185. pixeltable/share/protocol/common.py +165 -0
  186. pixeltable/share/protocol/operation_types.py +33 -0
  187. pixeltable/share/protocol/replica.py +119 -0
  188. pixeltable/share/publish.py +349 -0
  189. pixeltable/store.py +398 -232
  190. pixeltable/type_system.py +730 -267
  191. pixeltable/utils/__init__.py +40 -0
  192. pixeltable/utils/arrow.py +201 -29
  193. pixeltable/utils/av.py +298 -0
  194. pixeltable/utils/azure_store.py +346 -0
  195. pixeltable/utils/coco.py +26 -27
  196. pixeltable/utils/code.py +4 -4
  197. pixeltable/utils/console_output.py +46 -0
  198. pixeltable/utils/coroutine.py +24 -0
  199. pixeltable/utils/dbms.py +92 -0
  200. pixeltable/utils/description_helper.py +11 -12
  201. pixeltable/utils/documents.py +60 -61
  202. pixeltable/utils/exception_handler.py +36 -0
  203. pixeltable/utils/filecache.py +38 -22
  204. pixeltable/utils/formatter.py +88 -51
  205. pixeltable/utils/gcs_store.py +295 -0
  206. pixeltable/utils/http.py +133 -0
  207. pixeltable/utils/http_server.py +14 -13
  208. pixeltable/utils/iceberg.py +13 -0
  209. pixeltable/utils/image.py +17 -0
  210. pixeltable/utils/lancedb.py +90 -0
  211. pixeltable/utils/local_store.py +322 -0
  212. pixeltable/utils/misc.py +5 -0
  213. pixeltable/utils/object_stores.py +573 -0
  214. pixeltable/utils/pydantic.py +60 -0
  215. pixeltable/utils/pytorch.py +20 -20
  216. pixeltable/utils/s3_store.py +527 -0
  217. pixeltable/utils/sql.py +32 -5
  218. pixeltable/utils/system.py +30 -0
  219. pixeltable/utils/transactional_directory.py +4 -3
  220. pixeltable-0.5.7.dist-info/METADATA +579 -0
  221. pixeltable-0.5.7.dist-info/RECORD +227 -0
  222. {pixeltable-0.2.26.dist-info → pixeltable-0.5.7.dist-info}/WHEEL +1 -1
  223. pixeltable-0.5.7.dist-info/entry_points.txt +2 -0
  224. pixeltable/__version__.py +0 -3
  225. pixeltable/catalog/named_function.py +0 -36
  226. pixeltable/catalog/path_dict.py +0 -141
  227. pixeltable/dataframe.py +0 -894
  228. pixeltable/exec/expr_eval_node.py +0 -232
  229. pixeltable/ext/__init__.py +0 -14
  230. pixeltable/ext/functions/__init__.py +0 -8
  231. pixeltable/ext/functions/whisperx.py +0 -77
  232. pixeltable/ext/functions/yolox.py +0 -157
  233. pixeltable/tool/create_test_db_dump.py +0 -311
  234. pixeltable/tool/create_test_video.py +0 -81
  235. pixeltable/tool/doc_plugins/griffe.py +0 -50
  236. pixeltable/tool/doc_plugins/mkdocstrings.py +0 -6
  237. pixeltable/tool/doc_plugins/templates/material/udf.html.jinja +0 -135
  238. pixeltable/tool/embed_udf.py +0 -9
  239. pixeltable/tool/mypy_plugin.py +0 -55
  240. pixeltable/utils/media_store.py +0 -76
  241. pixeltable/utils/s3.py +0 -16
  242. pixeltable-0.2.26.dist-info/METADATA +0 -400
  243. pixeltable-0.2.26.dist-info/RECORD +0 -156
  244. pixeltable-0.2.26.dist-info/entry_points.txt +0 -3
  245. {pixeltable-0.2.26.dist-info → pixeltable-0.5.7.dist-info/licenses}/LICENSE +0 -0
@@ -1,5 +1,5 @@
1
1
  """
2
- Pixeltable [UDFs](https://pixeltable.readme.io/docs/user-defined-functions-udfs) for `ImageType`.
2
+ Pixeltable UDFs for `ImageType`.
3
3
 
4
4
  Example:
5
5
  ```python
@@ -10,14 +10,15 @@ t.select(t.img_col.convert('L')).collect()
10
10
  ```
11
11
  """
12
12
 
13
- import base64
14
- from typing import Optional
13
+ from typing import Any, Literal
15
14
 
16
15
  import PIL.Image
17
16
 
18
17
  import pixeltable as pxt
18
+ import pixeltable.type_system as ts
19
19
  from pixeltable.exprs import Expr
20
20
  from pixeltable.utils.code import local_public_names
21
+ from pixeltable.utils.image import to_base64
21
22
 
22
23
 
23
24
  @pxt.udf(is_method=True)
@@ -29,45 +30,42 @@ def b64_encode(img: PIL.Image.Image, image_format: str = 'png') -> str:
29
30
  img: image
30
31
  image_format: image format [supported by PIL](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#fully-supported-formats)
31
32
  """
32
- import io
33
+ return to_base64(img, format=image_format)
33
34
 
34
- bytes_arr = io.BytesIO()
35
- img.save(bytes_arr, format=image_format)
36
- b64_bytes = base64.b64encode(bytes_arr.getvalue())
37
- return b64_bytes.decode('utf-8')
38
35
 
39
-
40
- @pxt.udf(substitute_fn=PIL.Image.alpha_composite, is_method=True)
36
+ @pxt.udf(is_method=True)
41
37
  def alpha_composite(im1: PIL.Image.Image, im2: PIL.Image.Image) -> PIL.Image.Image:
42
38
  """
43
39
  Alpha composite `im2` over `im1`.
44
40
 
45
41
  Equivalent to [`PIL.Image.alpha_composite()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.alpha_composite)
46
42
  """
47
- pass
43
+ return PIL.Image.alpha_composite(im1, im2)
48
44
 
49
45
 
50
- @pxt.udf(substitute_fn=PIL.Image.blend, is_method=True)
46
+ @pxt.udf(is_method=True)
51
47
  def blend(im1: PIL.Image.Image, im2: PIL.Image.Image, alpha: float) -> PIL.Image.Image:
52
48
  """
53
49
  Return a new image by interpolating between two input images, using a constant alpha.
54
50
 
55
51
  Equivalent to [`PIL.Image.blend()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.blend)
56
52
  """
57
- pass
53
+ return PIL.Image.blend(im1, im2, alpha)
54
+
58
55
 
59
- @pxt.udf(substitute_fn=PIL.Image.composite, is_method=True)
56
+ @pxt.udf(is_method=True)
60
57
  def composite(image1: PIL.Image.Image, image2: PIL.Image.Image, mask: PIL.Image.Image) -> PIL.Image.Image:
61
58
  """
62
59
  Return a composite image by blending two images using a mask.
63
60
 
64
61
  Equivalent to [`PIL.Image.composite()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.composite)
65
62
  """
66
- pass
63
+ return PIL.Image.composite(image1, image2, mask)
67
64
 
68
65
 
69
66
  # PIL.Image.Image methods
70
67
 
68
+
71
69
  # Image.convert()
72
70
  @pxt.udf(is_method=True)
73
71
  def convert(self: PIL.Image.Image, mode: str) -> PIL.Image.Image:
@@ -78,20 +76,22 @@ def convert(self: PIL.Image.Image, mode: str) -> PIL.Image.Image:
78
76
  [`PIL.Image.Image.convert()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.convert).
79
77
 
80
78
  Args:
81
- mode: The mode to convert to. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes) for a list of supported modes.
79
+ mode: The mode to convert to. See the
80
+ [Pillow documentation](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes)
81
+ for a list of supported modes.
82
82
  """
83
83
  return self.convert(mode)
84
84
 
85
85
 
86
86
  @convert.conditional_return_type
87
- def _(self: Expr, mode: str) -> pxt.ColumnType:
87
+ def _(self: Expr, mode: str) -> ts.ColumnType:
88
88
  input_type = self.col_type
89
- assert isinstance(input_type, pxt.ImageType)
90
- return pxt.ImageType(size=input_type.size, mode=mode, nullable=input_type.nullable)
89
+ assert isinstance(input_type, ts.ImageType)
90
+ return ts.ImageType(size=input_type.size, mode=mode, nullable=input_type.nullable)
91
91
 
92
92
 
93
93
  # Image.crop()
94
- @pxt.udf(substitute_fn=PIL.Image.Image.crop, is_method=True)
94
+ @pxt.udf(is_method=True)
95
95
  def crop(self: PIL.Image.Image, box: tuple[int, int, int, int]) -> PIL.Image.Image:
96
96
  """
97
97
  Return a rectangular region from the image. The box is a 4-tuple defining the left, upper, right, and lower pixel
@@ -100,20 +100,20 @@ def crop(self: PIL.Image.Image, box: tuple[int, int, int, int]) -> PIL.Image.Ima
100
100
  Equivalent to
101
101
  [`PIL.Image.Image.crop()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.crop)
102
102
  """
103
- pass
103
+ return self.crop(box)
104
104
 
105
105
 
106
106
  @crop.conditional_return_type
107
- def _(self: Expr, box: tuple[int, int, int, int]) -> pxt.ColumnType:
107
+ def _(self: Expr, box: tuple[int, int, int, int]) -> ts.ColumnType:
108
108
  input_type = self.col_type
109
- assert isinstance(input_type, pxt.ImageType)
110
- if (isinstance(box, list) or isinstance(box, tuple)) and len(box) == 4 and all(isinstance(x, int) for x in box):
111
- return pxt.ImageType(size=(box[2] - box[0], box[3] - box[1]), mode=input_type.mode, nullable=input_type.nullable)
112
- return pxt.ImageType(mode=input_type.mode, nullable=input_type.nullable) # we can't compute the size statically
109
+ assert isinstance(input_type, ts.ImageType)
110
+ if (isinstance(box, (list, tuple))) and len(box) == 4 and all(isinstance(x, int) for x in box):
111
+ return ts.ImageType(size=(box[2] - box[0], box[3] - box[1]), mode=input_type.mode, nullable=input_type.nullable)
112
+ return ts.ImageType(mode=input_type.mode, nullable=input_type.nullable) # we can't compute the size statically
113
113
 
114
114
 
115
115
  # Image.getchannel()
116
- @pxt.udf(substitute_fn=PIL.Image.Image.getchannel, is_method=True)
116
+ @pxt.udf(is_method=True)
117
117
  def getchannel(self: PIL.Image.Image, channel: int) -> PIL.Image.Image:
118
118
  """
119
119
  Return an L-mode image containing a single channel of the original image.
@@ -124,7 +124,14 @@ def getchannel(self: PIL.Image.Image, channel: int) -> PIL.Image.Image:
124
124
  Args:
125
125
  channel: The channel to extract. This is a 0-based index.
126
126
  """
127
- pass
127
+ return self.getchannel(channel)
128
+
129
+
130
+ @getchannel.conditional_return_type
131
+ def _(self: Expr) -> ts.ColumnType:
132
+ input_type = self.col_type
133
+ assert isinstance(input_type, ts.ImageType)
134
+ return ts.ImageType(size=input_type.size, mode='L', nullable=input_type.nullable)
128
135
 
129
136
 
130
137
  @pxt.udf(is_method=True)
@@ -142,16 +149,9 @@ def get_metadata(self: PIL.Image.Image) -> dict:
142
149
  }
143
150
 
144
151
 
145
- @getchannel.conditional_return_type
146
- def _(self: Expr) -> pxt.ColumnType:
147
- input_type = self.col_type
148
- assert isinstance(input_type, pxt.ImageType)
149
- return pxt.ImageType(size=input_type.size, mode='L', nullable=input_type.nullable)
150
-
151
-
152
152
  # Image.point()
153
153
  @pxt.udf(is_method=True)
154
- def point(self: PIL.Image.Image, lut: list[int], mode: Optional[str] = None) -> PIL.Image.Image:
154
+ def point(self: PIL.Image.Image, lut: list[int], mode: str | None = None) -> PIL.Image.Image:
155
155
  """
156
156
  Map image pixels through a lookup table.
157
157
 
@@ -177,10 +177,10 @@ def resize(self: PIL.Image.Image, size: tuple[int, int]) -> PIL.Image.Image:
177
177
 
178
178
 
179
179
  @resize.conditional_return_type
180
- def _(self: Expr, size: tuple[int, int]) -> pxt.ColumnType:
180
+ def _(self: Expr, size: tuple[int, int]) -> ts.ColumnType:
181
181
  input_type = self.col_type
182
- assert isinstance(input_type, pxt.ImageType)
183
- return pxt.ImageType(size=size, mode=input_type.mode, nullable=input_type.nullable)
182
+ assert isinstance(input_type, ts.ImageType)
183
+ return ts.ImageType(size=size, mode=input_type.mode, nullable=input_type.nullable)
184
184
 
185
185
 
186
186
  # Image.rotate()
@@ -198,7 +198,7 @@ def rotate(self: PIL.Image.Image, angle: int) -> PIL.Image.Image:
198
198
  return self.rotate(angle)
199
199
 
200
200
 
201
- @pxt.udf(substitute_fn=PIL.Image.Image.effect_spread, is_method=True)
201
+ @pxt.udf(is_method=True)
202
202
  def effect_spread(self: PIL.Image.Image, distance: int) -> PIL.Image.Image:
203
203
  """
204
204
  Randomly spread pixels in an image.
@@ -209,11 +209,11 @@ def effect_spread(self: PIL.Image.Image, distance: int) -> PIL.Image.Image:
209
209
  Args:
210
210
  distance: The distance to spread pixels.
211
211
  """
212
- pass
212
+ return self.effect_spread(distance)
213
213
 
214
214
 
215
- @pxt.udf(substitute_fn=PIL.Image.Image.transpose, is_method=True)
216
- def transpose(self: PIL.Image.Image, method: int) -> PIL.Image.Image:
215
+ @pxt.udf(is_method=True)
216
+ def transpose(self: PIL.Image.Image, method: Literal[0, 1, 2, 3, 4, 5, 6]) -> PIL.Image.Image:
217
217
  """
218
218
  Transpose the image.
219
219
 
@@ -221,20 +221,22 @@ def transpose(self: PIL.Image.Image, method: int) -> PIL.Image.Image:
221
221
  [`PIL.Image.Image.transpose()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.transpose)
222
222
 
223
223
  Args:
224
- method: The transpose method. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.transpose) for a list of supported methods.
224
+ method: The transpose method. See the
225
+ [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.transpose)
226
+ for a list of supported methods.
225
227
  """
226
- pass
228
+ return self.transpose(method)
227
229
 
228
230
 
229
231
  @rotate.conditional_return_type
230
232
  @effect_spread.conditional_return_type
231
233
  @transpose.conditional_return_type
232
- def _(self: Expr) -> pxt.ColumnType:
234
+ def _(self: Expr) -> ts.ColumnType:
233
235
  return self.col_type
234
236
 
235
237
 
236
- @pxt.udf(substitute_fn=PIL.Image.Image.entropy, is_method=True)
237
- def entropy(self: PIL.Image.Image, mask: Optional[PIL.Image.Image] = None, extrema: Optional[list] = None) -> float:
238
+ @pxt.udf(is_method=True)
239
+ def entropy(self: PIL.Image.Image, mask: PIL.Image.Image | None = None, extrema: list | None = None) -> float:
238
240
  """
239
241
  Returns the entropy of the image, optionally using a mask and extrema.
240
242
 
@@ -245,35 +247,36 @@ def entropy(self: PIL.Image.Image, mask: Optional[PIL.Image.Image] = None, extre
245
247
  mask: An optional mask image.
246
248
  extrema: An optional list of extrema.
247
249
  """
248
- pass
250
+ return self.entropy(mask, extrema) # type: ignore[arg-type]
249
251
 
250
252
 
251
- @pxt.udf(substitute_fn=PIL.Image.Image.getbands, is_method=True)
252
- def getbands(self: PIL.Image.Image) -> tuple[str]:
253
+ @pxt.udf(is_method=True)
254
+ def getbands(self: PIL.Image.Image) -> tuple[str, ...]:
253
255
  """
254
256
  Return a tuple containing the names of the image bands.
255
257
 
256
258
  Equivalent to
257
259
  [`PIL.Image.Image.getbands()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getbands)
258
260
  """
259
- pass
261
+ return self.getbands()
260
262
 
261
263
 
262
- @pxt.udf(substitute_fn=PIL.Image.Image.getbbox, is_method=True)
263
- def getbbox(self: PIL.Image.Image, *, alpha_only: bool = True) -> tuple[int, int, int, int]:
264
+ @pxt.udf(is_method=True)
265
+ def getbbox(self: PIL.Image.Image, *, alpha_only: bool = True) -> tuple[int, int, int, int] | None:
264
266
  """
265
267
  Return a bounding box for the non-zero regions of the image.
266
268
 
267
269
  Equivalent to [`PIL.Image.Image.getbbox()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getbbox)
268
270
 
269
271
  Args:
270
- alpha_only: If `True`, and the image has an alpha channel, trim transparent pixels. Otherwise, trim pixels when all channels are zero.
272
+ alpha_only: If `True`, and the image has an alpha channel, trim transparent pixels. Otherwise,
273
+ trim pixels when all channels are zero.
271
274
  """
272
- pass
275
+ return self.getbbox(alpha_only=alpha_only)
273
276
 
274
277
 
275
- @pxt.udf(substitute_fn=PIL.Image.Image.getcolors, is_method=True)
276
- def getcolors(self: PIL.Image.Image, maxcolors: int = 256) -> tuple[tuple[int, int, int], int]:
278
+ @pxt.udf(is_method=True)
279
+ def getcolors(self: PIL.Image.Image, maxcolors: int = 256) -> list[tuple[int, int]]:
277
280
  """
278
281
  Return a list of colors used in the image, up to a maximum of `maxcolors`.
279
282
 
@@ -283,10 +286,10 @@ def getcolors(self: PIL.Image.Image, maxcolors: int = 256) -> tuple[tuple[int, i
283
286
  Args:
284
287
  maxcolors: The maximum number of colors to return.
285
288
  """
286
- pass
289
+ return self.getcolors(maxcolors)
287
290
 
288
291
 
289
- @pxt.udf(substitute_fn=PIL.Image.Image.getextrema, is_method=True)
292
+ @pxt.udf(is_method=True)
290
293
  def getextrema(self: PIL.Image.Image) -> tuple[int, int]:
291
294
  """
292
295
  Return a 2-tuple containing the minimum and maximum pixel values of the image.
@@ -294,11 +297,11 @@ def getextrema(self: PIL.Image.Image) -> tuple[int, int]:
294
297
  Equivalent to
295
298
  [`PIL.Image.Image.getextrema()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getextrema)
296
299
  """
297
- pass
300
+ return self.getextrema()
298
301
 
299
302
 
300
- @pxt.udf(substitute_fn=PIL.Image.Image.getpalette, is_method=True)
301
- def getpalette(self: PIL.Image.Image, mode: Optional[str] = None) -> tuple[int]:
303
+ @pxt.udf(is_method=True)
304
+ def getpalette(self: PIL.Image.Image, mode: str | None = None) -> list[int] | None:
302
305
  """
303
306
  Return the palette of the image, optionally converting it to a different mode.
304
307
 
@@ -308,7 +311,7 @@ def getpalette(self: PIL.Image.Image, mode: Optional[str] = None) -> tuple[int]:
308
311
  Args:
309
312
  mode: The mode to convert the palette to.
310
313
  """
311
- pass
314
+ return self.getpalette(mode)
312
315
 
313
316
 
314
317
  @pxt.udf(is_method=True)
@@ -326,21 +329,19 @@ def getpixel(self: PIL.Image.Image, xy: list) -> tuple[int]:
326
329
  return self.getpixel(tuple(xy))
327
330
 
328
331
 
329
- @pxt.udf(substitute_fn=PIL.Image.Image.getprojection, is_method=True)
330
- def getprojection(self: PIL.Image.Image) -> tuple[int]:
332
+ @pxt.udf(is_method=True)
333
+ def getprojection(self: PIL.Image.Image) -> tuple[list[int], list[int]]:
331
334
  """
332
335
  Return two sequences representing the horizontal and vertical projection of the image.
333
336
 
334
337
  Equivalent to
335
338
  [`PIL.Image.Image.getprojection()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getprojection)
336
339
  """
337
- pass
340
+ return self.getprojection()
338
341
 
339
342
 
340
- @pxt.udf(substitute_fn=PIL.Image.Image.histogram, is_method=True)
341
- def histogram(
342
- self: PIL.Image.Image, mask: Optional[PIL.Image.Image] = None, extrema: Optional[list] = None
343
- ) -> list[int]:
343
+ @pxt.udf(is_method=True)
344
+ def histogram(self: PIL.Image.Image, mask: PIL.Image.Image | None = None, extrema: list | None = None) -> list[int]:
344
345
  """
345
346
  Return a histogram for the image.
346
347
 
@@ -351,16 +352,16 @@ def histogram(
351
352
  mask: An optional mask image.
352
353
  extrema: An optional list of extrema.
353
354
  """
354
- pass
355
+ return self.histogram(mask, extrema) # type: ignore[arg-type]
355
356
 
356
357
 
357
- @pxt.udf(substitute_fn=PIL.Image.Image.quantize, is_method=True)
358
+ @pxt.udf(is_method=True)
358
359
  def quantize(
359
360
  self: PIL.Image.Image,
360
361
  colors: int = 256,
361
- method: Optional[int] = None,
362
+ method: Literal[0, 1, 2, 3] | None = None,
362
363
  kmeans: int = 0,
363
- palette: Optional[int] = None,
364
+ palette: PIL.Image.Image | None = None,
364
365
  dither: int = PIL.Image.Dither.FLOYDSTEINBERG,
365
366
  ) -> PIL.Image.Image:
366
367
  """
@@ -371,16 +372,20 @@ def quantize(
371
372
 
372
373
  Args:
373
374
  colors: The number of colors to quantize to.
374
- method: The quantization method. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.quantize) for a list of supported methods.
375
+ method: The quantization method. See the
376
+ [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.quantize)
377
+ for a list of supported methods.
375
378
  kmeans: The number of k-means clusters to use.
376
379
  palette: The palette to use.
377
- dither: The dithering method. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.quantize) for a list of supported methods.
378
- """
379
- pass
380
+ dither: The dithering method. See the
381
+ [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.quantize)
382
+ for a list of supported methods.
383
+ """
384
+ return self.quantize(colors, method, kmeans, palette, dither)
380
385
 
381
386
 
382
- @pxt.udf(substitute_fn=PIL.Image.Image.reduce, is_method=True)
383
- def reduce(self: PIL.Image.Image, factor: int, box: Optional[tuple[int, int, int, int]] = None) -> PIL.Image.Image:
387
+ @pxt.udf(is_method=True)
388
+ def reduce(self: PIL.Image.Image, factor: int, box: tuple[int, int, int, int] | None = None) -> PIL.Image.Image:
384
389
  """
385
390
  Reduce the image by the given factor.
386
391
 
@@ -389,28 +394,94 @@ def reduce(self: PIL.Image.Image, factor: int, box: Optional[tuple[int, int, int
389
394
 
390
395
  Args:
391
396
  factor: The reduction factor.
392
- box: An optional 4-tuple of ints providing the source image region to be reduced. The values must be within (0, 0, width, height) rectangle. If omitted or None, the entire source is used.
397
+ box: An optional 4-tuple of ints providing the source image region to be reduced. The values must be within
398
+ (0, 0, width, height) rectangle. If omitted or None, the entire source is used.
399
+ """
400
+ return self.reduce(factor, box)
401
+
402
+
403
+ @pxt.udf(is_method=True)
404
+ def thumbnail(
405
+ self: PIL.Image.Image,
406
+ size: tuple[int, int],
407
+ resample: int = PIL.Image.Resampling.LANCZOS,
408
+ reducing_gap: float | None = 2.0,
409
+ ) -> PIL.Image.Image:
393
410
  """
394
- pass
411
+ Create a thumbnail of the image.
412
+
413
+ Equivalent to
414
+ [`PIL.Image.Image.thumbnail()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.thumbnail)
415
+
416
+ Args:
417
+ size: The size of the thumbnail, as a tuple of (width, height).
418
+ resample: The resampling filter to use. See the
419
+ [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.thumbnail)
420
+ for a list of supported filters.
421
+ reducing_gap: The reducing gap to use.
422
+ """
423
+ result = self.copy()
424
+ result.thumbnail(size, PIL.Image.Resampling(resample), reducing_gap)
425
+ return result
395
426
 
396
427
 
397
428
  @pxt.udf(is_property=True)
398
429
  def width(self: PIL.Image.Image) -> int:
430
+ """
431
+ Return the width of the image.
432
+ """
399
433
  return self.width
400
434
 
401
435
 
402
436
  @pxt.udf(is_property=True)
403
437
  def height(self: PIL.Image.Image) -> int:
438
+ """
439
+ Return the height of the image.
440
+ """
404
441
  return self.height
405
442
 
406
443
 
407
444
  @pxt.udf(is_property=True)
408
445
  def mode(self: PIL.Image.Image) -> str:
446
+ """
447
+ Return the image mode.
448
+ """
409
449
  return self.mode
410
450
 
411
451
 
452
+ def tile_iterator(
453
+ image: Any, tile_size: tuple[int, int], *, overlap: tuple[int, int] = (0, 0)
454
+ ) -> tuple[type[pxt.iterators.ComponentIterator], dict[str, Any]]:
455
+ """
456
+ Iterator over tiles of an image. Each image will be divided into tiles of size `tile_size`, and the tiles will be
457
+ iterated over in row-major order (left-to-right, then top-to-bottom). An optional `overlap` parameter may be
458
+ specified. If the tiles do not exactly cover the image, then the rightmost and bottommost tiles will be padded with
459
+ blackspace, so that the output images all have the exact size `tile_size`.
460
+
461
+ Args:
462
+ image: Image to split into tiles.
463
+ tile_size: Size of each tile, as a pair of integers `[width, height]`.
464
+ overlap: Amount of overlap between adjacent tiles, as a pair of integers `[width, height]`.
465
+
466
+ Examples:
467
+ This example assumes an existing table `tbl` with a column `img` of type `pxt.Image`.
468
+
469
+ Create a view that splits all images into 256x256 tiles with 32 pixels of overlap:
470
+
471
+ >>> pxt.create_view(
472
+ ... 'image_tiles',
473
+ ... tbl,
474
+ ... iterator=image_tile_iterator(tbl.img, tile_size=(256, 256), overlap=(32, 32))
475
+ ... )
476
+ """
477
+ kwargs: dict[str, Any] = {}
478
+ if overlap != (0, 0):
479
+ kwargs['overlap'] = overlap
480
+ return pxt.iterators.image.TileIterator._create(image=image, tile_size=tile_size, **kwargs)
481
+
482
+
412
483
  __all__ = local_public_names(__name__)
413
484
 
414
485
 
415
- def __dir__():
486
+ def __dir__() -> list[str]:
416
487
  return __all__
@@ -1,12 +1,13 @@
1
1
  """
2
- Pixeltable [UDFs](https://pixeltable.readme.io/docs/user-defined-functions-udfs) for `JsonType`.
2
+ Pixeltable UDFs for `JsonType`.
3
3
 
4
4
  Example:
5
5
  ```python
6
6
  import pixeltable as pxt
7
+ import pixeltable.functions as pxtf
7
8
 
8
9
  t = pxt.get_table(...)
9
- t.select(pxt.functions.json.make_list()).collect()
10
+ t.select(pxtf.json.make_list(t.json_col)).collect()
10
11
  ```
11
12
  """
12
13
 
@@ -16,20 +17,16 @@ import pixeltable as pxt
16
17
  from pixeltable.utils.code import local_public_names
17
18
 
18
19
 
19
- @pxt.uda(
20
- update_types=[pxt.JsonType(nullable=True)],
21
- value_type=pxt.JsonType(),
22
- requires_order_by=False,
23
- allows_window=False,
24
- )
20
+ @pxt.uda
25
21
  class make_list(pxt.Aggregator):
26
22
  """
27
23
  Collects arguments into a list.
28
24
  """
29
- def __init__(self):
25
+
26
+ def __init__(self) -> None:
30
27
  self.output: list[Any] = []
31
28
 
32
- def update(self, obj: Any) -> None:
29
+ def update(self, obj: pxt.Json) -> None:
33
30
  if obj is None:
34
31
  return
35
32
  self.output.append(obj)
@@ -41,5 +38,5 @@ class make_list(pxt.Aggregator):
41
38
  __all__ = local_public_names(__name__)
42
39
 
43
40
 
44
- def __dir__():
41
+ def __dir__() -> list[str]:
45
42
  return __all__
@@ -1,5 +1,12 @@
1
+ """
2
+ Pixeltable UDFs for llama.cpp models.
3
+
4
+ Provides integration with llama.cpp for running quantized language models locally,
5
+ supporting chat completions and embeddings with GGUF format models.
6
+ """
7
+
1
8
  from pathlib import Path
2
- from typing import TYPE_CHECKING, Any, Optional
9
+ from typing import TYPE_CHECKING, Any
3
10
 
4
11
  import pixeltable as pxt
5
12
  import pixeltable.exceptions as excs
@@ -14,10 +21,10 @@ if TYPE_CHECKING:
14
21
  def create_chat_completion(
15
22
  messages: list[dict],
16
23
  *,
17
- model_path: Optional[str] = None,
18
- repo_id: Optional[str] = None,
19
- repo_filename: Optional[str] = None,
20
- args: Optional[dict[str, Any]] = None,
24
+ model_path: str | None = None,
25
+ repo_id: str | None = None,
26
+ repo_filename: str | None = None,
27
+ model_kwargs: dict[str, Any] | None = None,
21
28
  ) -> dict:
22
29
  """
23
30
  Generate a chat completion from a list of messages.
@@ -35,14 +42,14 @@ def create_chat_completion(
35
42
  repo_id: The Hugging Face model repo id (if using a pretrained model).
36
43
  repo_filename: A filename or glob pattern to match the model file in the repo (optional, if using a
37
44
  pretrained model).
38
- args: Additional arguments to pass to the `create_chat_completions` call, such as `max_tokens`, `temperature`,
39
- `top_p`, and `top_k`. For details, see the
45
+ model_kwargs: Additional keyword args for the llama_cpp `create_chat_completions` API, such as `max_tokens`,
46
+ `temperature`, `top_p`, and `top_k`. For details, see the
40
47
  [llama_cpp create_chat_completions documentation](https://llama-cpp-python.readthedocs.io/en/latest/api-reference/#llama_cpp.Llama.create_chat_completion).
41
48
  """
42
49
  Env.get().require_package('llama_cpp', min_version=[0, 3, 1])
43
50
 
44
- if args is None:
45
- args = {}
51
+ if model_kwargs is None:
52
+ model_kwargs = {}
46
53
 
47
54
  if (model_path is None) == (repo_id is None):
48
55
  raise excs.Error('Exactly one of `model_path` or `repo_id` must be provided.')
@@ -56,13 +63,13 @@ def create_chat_completion(
56
63
  else:
57
64
  Env.get().require_package('huggingface_hub')
58
65
  llm = _lookup_pretrained_model(repo_id, repo_filename, n_gpu_layers)
59
- return llm.create_chat_completion(messages, **args) # type: ignore
66
+ return llm.create_chat_completion(messages, **model_kwargs) # type: ignore
60
67
 
61
68
 
62
69
  def _is_gpu_available() -> bool:
63
70
  import llama_cpp
64
71
 
65
- global _IS_GPU_AVAILABLE
72
+ global _IS_GPU_AVAILABLE # noqa: PLW0603
66
73
  if _IS_GPU_AVAILABLE is None:
67
74
  llama_cpp_path = Path(llama_cpp.__file__).parent
68
75
  lib = llama_cpp.llama_cpp.load_shared_library('llama', llama_cpp_path / 'lib')
@@ -81,27 +88,32 @@ def _lookup_local_model(model_path: str, n_gpu_layers: int) -> 'llama_cpp.Llama'
81
88
  return _model_cache[key]
82
89
 
83
90
 
84
- def _lookup_pretrained_model(repo_id: str, filename: Optional[str], n_gpu_layers: int) -> 'llama_cpp.Llama':
91
+ def _lookup_pretrained_model(repo_id: str, filename: str | None, n_gpu_layers: int) -> 'llama_cpp.Llama':
85
92
  import llama_cpp
86
93
 
87
94
  key = (repo_id, filename, n_gpu_layers)
88
95
  if key not in _model_cache:
89
96
  llm = llama_cpp.Llama.from_pretrained(
90
- repo_id=repo_id,
91
- filename=filename,
92
- n_gpu_layers=n_gpu_layers,
93
- verbose=False,
97
+ repo_id=repo_id, filename=filename, n_gpu_layers=n_gpu_layers, verbose=False
94
98
  )
95
99
  _model_cache[key] = llm
96
100
  return _model_cache[key]
97
101
 
98
102
 
99
- _model_cache: dict[tuple[str, str, int], Any] = {}
100
- _IS_GPU_AVAILABLE: Optional[bool] = None
103
+ _model_cache: dict[tuple[str, str, int], 'llama_cpp.Llama'] = {}
104
+ _IS_GPU_AVAILABLE: bool | None = None
105
+
106
+
107
+ def cleanup() -> None:
108
+ for model in _model_cache.values():
109
+ if model._sampler is not None:
110
+ model._sampler.close()
111
+ model.close()
112
+ _model_cache.clear()
101
113
 
102
114
 
103
115
  __all__ = local_public_names(__name__)
104
116
 
105
117
 
106
- def __dir__():
118
+ def __dir__() -> list[str]:
107
119
  return __all__