pixeltable 0.2.5__py3-none-any.whl → 0.2.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.
Potentially problematic release.
This version of pixeltable might be problematic. Click here for more details.
- pixeltable/__init__.py +20 -9
- pixeltable/__version__.py +3 -0
- pixeltable/catalog/column.py +23 -7
- pixeltable/catalog/insertable_table.py +32 -19
- pixeltable/catalog/table.py +210 -20
- pixeltable/catalog/table_version.py +272 -111
- pixeltable/catalog/table_version_path.py +6 -1
- pixeltable/dataframe.py +184 -110
- pixeltable/datatransfer/__init__.py +1 -0
- pixeltable/datatransfer/label_studio.py +526 -0
- pixeltable/datatransfer/remote.py +113 -0
- pixeltable/env.py +213 -79
- pixeltable/exec/__init__.py +2 -1
- pixeltable/exec/data_row_batch.py +6 -7
- pixeltable/exec/expr_eval_node.py +28 -28
- pixeltable/exec/sql_scan_node.py +7 -6
- pixeltable/exprs/__init__.py +4 -3
- pixeltable/exprs/column_ref.py +11 -2
- pixeltable/exprs/comparison.py +39 -1
- pixeltable/exprs/data_row.py +7 -0
- pixeltable/exprs/expr.py +26 -19
- pixeltable/exprs/function_call.py +17 -18
- pixeltable/exprs/globals.py +14 -2
- pixeltable/exprs/image_member_access.py +9 -28
- pixeltable/exprs/in_predicate.py +96 -0
- pixeltable/exprs/inline_array.py +13 -11
- pixeltable/exprs/inline_dict.py +15 -13
- pixeltable/exprs/row_builder.py +7 -1
- pixeltable/exprs/similarity_expr.py +67 -0
- pixeltable/ext/functions/whisperx.py +30 -0
- pixeltable/ext/functions/yolox.py +16 -0
- pixeltable/func/__init__.py +0 -2
- pixeltable/func/aggregate_function.py +5 -2
- pixeltable/func/callable_function.py +57 -13
- pixeltable/func/expr_template_function.py +14 -3
- pixeltable/func/function.py +35 -4
- pixeltable/func/signature.py +5 -15
- pixeltable/func/udf.py +8 -12
- pixeltable/functions/fireworks.py +9 -4
- pixeltable/functions/huggingface.py +48 -5
- pixeltable/functions/openai.py +49 -11
- pixeltable/functions/pil/image.py +61 -64
- pixeltable/functions/together.py +32 -6
- pixeltable/functions/util.py +0 -43
- pixeltable/functions/video.py +46 -8
- pixeltable/globals.py +443 -0
- pixeltable/index/__init__.py +1 -0
- pixeltable/index/base.py +9 -2
- pixeltable/index/btree.py +54 -0
- pixeltable/index/embedding_index.py +91 -15
- pixeltable/io/__init__.py +4 -0
- pixeltable/io/globals.py +59 -0
- pixeltable/{utils → io}/hf_datasets.py +48 -17
- pixeltable/io/pandas.py +148 -0
- pixeltable/{utils → io}/parquet.py +58 -33
- pixeltable/iterators/__init__.py +1 -1
- pixeltable/iterators/base.py +8 -4
- pixeltable/iterators/document.py +225 -93
- pixeltable/iterators/video.py +16 -9
- pixeltable/metadata/__init__.py +8 -4
- pixeltable/metadata/converters/convert_12.py +3 -0
- pixeltable/metadata/converters/convert_13.py +41 -0
- pixeltable/metadata/converters/convert_14.py +13 -0
- pixeltable/metadata/converters/convert_15.py +29 -0
- pixeltable/metadata/converters/util.py +63 -0
- pixeltable/metadata/schema.py +12 -6
- pixeltable/plan.py +11 -24
- pixeltable/store.py +16 -23
- pixeltable/tool/create_test_db_dump.py +49 -14
- pixeltable/type_system.py +27 -58
- pixeltable/utils/coco.py +94 -0
- pixeltable/utils/documents.py +42 -12
- pixeltable/utils/http_server.py +70 -0
- pixeltable-0.2.7.dist-info/METADATA +137 -0
- pixeltable-0.2.7.dist-info/RECORD +126 -0
- {pixeltable-0.2.5.dist-info → pixeltable-0.2.7.dist-info}/WHEEL +1 -1
- pixeltable/client.py +0 -600
- pixeltable/exprs/image_similarity_predicate.py +0 -58
- pixeltable/func/batched_function.py +0 -53
- pixeltable/func/nos_function.py +0 -202
- pixeltable/tests/conftest.py +0 -171
- pixeltable/tests/ext/test_yolox.py +0 -21
- pixeltable/tests/functions/test_fireworks.py +0 -43
- pixeltable/tests/functions/test_functions.py +0 -60
- pixeltable/tests/functions/test_huggingface.py +0 -158
- pixeltable/tests/functions/test_openai.py +0 -162
- pixeltable/tests/functions/test_together.py +0 -112
- pixeltable/tests/test_audio.py +0 -65
- pixeltable/tests/test_catalog.py +0 -27
- pixeltable/tests/test_client.py +0 -21
- pixeltable/tests/test_component_view.py +0 -379
- pixeltable/tests/test_dataframe.py +0 -440
- pixeltable/tests/test_dirs.py +0 -107
- pixeltable/tests/test_document.py +0 -120
- pixeltable/tests/test_exprs.py +0 -802
- pixeltable/tests/test_function.py +0 -332
- pixeltable/tests/test_index.py +0 -138
- pixeltable/tests/test_migration.py +0 -44
- pixeltable/tests/test_nos.py +0 -54
- pixeltable/tests/test_snapshot.py +0 -231
- pixeltable/tests/test_table.py +0 -1343
- pixeltable/tests/test_transactional_directory.py +0 -42
- pixeltable/tests/test_types.py +0 -52
- pixeltable/tests/test_video.py +0 -159
- pixeltable/tests/test_view.py +0 -535
- pixeltable/tests/utils.py +0 -442
- pixeltable/utils/clip.py +0 -18
- pixeltable-0.2.5.dist-info/METADATA +0 -128
- pixeltable-0.2.5.dist-info/RECORD +0 -139
- {pixeltable-0.2.5.dist-info → pixeltable-0.2.7.dist-info}/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Callable, TypeVar, Optional
|
|
1
|
+
from typing import Callable, TypeVar, Optional, Any
|
|
2
2
|
|
|
3
3
|
import PIL.Image
|
|
4
4
|
import numpy as np
|
|
@@ -14,6 +14,7 @@ from pixeltable.functions.util import resolve_torch_device
|
|
|
14
14
|
def sentence_transformer(
|
|
15
15
|
sentences: Batch[str], *, model_id: str, normalize_embeddings: bool = False
|
|
16
16
|
) -> Batch[np.ndarray]:
|
|
17
|
+
"""Runs the specified sentence transformer model."""
|
|
17
18
|
env.Env.get().require_package('sentence_transformers')
|
|
18
19
|
from sentence_transformers import SentenceTransformer
|
|
19
20
|
|
|
@@ -23,6 +24,16 @@ def sentence_transformer(
|
|
|
23
24
|
return [array[i] for i in range(array.shape[0])]
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
@sentence_transformer.conditional_return_type
|
|
28
|
+
def _(model_id: str) -> ts.ArrayType:
|
|
29
|
+
try:
|
|
30
|
+
from sentence_transformers import SentenceTransformer
|
|
31
|
+
model = _lookup_model(model_id, SentenceTransformer)
|
|
32
|
+
return ts.ArrayType((model.get_sentence_embedding_dimension(),), dtype=ts.FloatType(), nullable=False)
|
|
33
|
+
except ImportError:
|
|
34
|
+
return ts.ArrayType((None,), dtype=ts.FloatType(), nullable=False)
|
|
35
|
+
|
|
36
|
+
|
|
26
37
|
@pxt.udf
|
|
27
38
|
def sentence_transformer_list(sentences: list, *, model_id: str, normalize_embeddings: bool = False) -> list:
|
|
28
39
|
env.Env.get().require_package('sentence_transformers')
|
|
@@ -36,6 +47,7 @@ def sentence_transformer_list(sentences: list, *, model_id: str, normalize_embed
|
|
|
36
47
|
|
|
37
48
|
@pxt.udf(batch_size=32)
|
|
38
49
|
def cross_encoder(sentences1: Batch[str], sentences2: Batch[str], *, model_id: str) -> Batch[float]:
|
|
50
|
+
"""Runs the specified cross-encoder model."""
|
|
39
51
|
env.Env.get().require_package('sentence_transformers')
|
|
40
52
|
from sentence_transformers import CrossEncoder
|
|
41
53
|
|
|
@@ -56,15 +68,15 @@ def cross_encoder_list(sentence1: str, sentences2: list, *, model_id: str) -> li
|
|
|
56
68
|
return array.tolist()
|
|
57
69
|
|
|
58
70
|
|
|
59
|
-
@pxt.udf(batch_size=32, return_type=ts.ArrayType((
|
|
71
|
+
@pxt.udf(batch_size=32, return_type=ts.ArrayType((None,), dtype=ts.FloatType(), nullable=False))
|
|
60
72
|
def clip_text(text: Batch[str], *, model_id: str) -> Batch[np.ndarray]:
|
|
73
|
+
"""Runs the specified CLIP model on text."""
|
|
61
74
|
env.Env.get().require_package('transformers')
|
|
62
75
|
device = resolve_torch_device('auto')
|
|
63
76
|
import torch
|
|
64
77
|
from transformers import CLIPModel, CLIPProcessor
|
|
65
78
|
|
|
66
79
|
model = _lookup_model(model_id, CLIPModel.from_pretrained, device=device)
|
|
67
|
-
assert model.config.projection_dim == 512
|
|
68
80
|
processor = _lookup_processor(model_id, CLIPProcessor.from_pretrained)
|
|
69
81
|
|
|
70
82
|
with torch.no_grad():
|
|
@@ -74,15 +86,15 @@ def clip_text(text: Batch[str], *, model_id: str) -> Batch[np.ndarray]:
|
|
|
74
86
|
return [embeddings[i] for i in range(embeddings.shape[0])]
|
|
75
87
|
|
|
76
88
|
|
|
77
|
-
@pxt.udf(batch_size=32, return_type=ts.ArrayType((
|
|
89
|
+
@pxt.udf(batch_size=32, return_type=ts.ArrayType((None,), dtype=ts.FloatType(), nullable=False))
|
|
78
90
|
def clip_image(image: Batch[PIL.Image.Image], *, model_id: str) -> Batch[np.ndarray]:
|
|
91
|
+
"""Runs the specified CLIP model on images."""
|
|
79
92
|
env.Env.get().require_package('transformers')
|
|
80
93
|
device = resolve_torch_device('auto')
|
|
81
94
|
import torch
|
|
82
95
|
from transformers import CLIPModel, CLIPProcessor
|
|
83
96
|
|
|
84
97
|
model = _lookup_model(model_id, CLIPModel.from_pretrained, device=device)
|
|
85
|
-
assert model.config.projection_dim == 512
|
|
86
98
|
processor = _lookup_processor(model_id, CLIPProcessor.from_pretrained)
|
|
87
99
|
|
|
88
100
|
with torch.no_grad():
|
|
@@ -92,8 +104,20 @@ def clip_image(image: Batch[PIL.Image.Image], *, model_id: str) -> Batch[np.ndar
|
|
|
92
104
|
return [embeddings[i] for i in range(embeddings.shape[0])]
|
|
93
105
|
|
|
94
106
|
|
|
107
|
+
@clip_text.conditional_return_type
|
|
108
|
+
@clip_image.conditional_return_type
|
|
109
|
+
def _(model_id: str) -> ts.ArrayType:
|
|
110
|
+
try:
|
|
111
|
+
from transformers import CLIPModel
|
|
112
|
+
model = _lookup_model(model_id, CLIPModel.from_pretrained)
|
|
113
|
+
return ts.ArrayType((model.config.projection_dim,), dtype=ts.FloatType(), nullable=False)
|
|
114
|
+
except ImportError:
|
|
115
|
+
return ts.ArrayType((None,), dtype=ts.FloatType(), nullable=False)
|
|
116
|
+
|
|
117
|
+
|
|
95
118
|
@pxt.udf(batch_size=4)
|
|
96
119
|
def detr_for_object_detection(image: Batch[PIL.Image.Image], *, model_id: str, threshold: float = 0.5) -> Batch[dict]:
|
|
120
|
+
"""Runs the specified DETR model."""
|
|
97
121
|
env.Env.get().require_package('transformers')
|
|
98
122
|
device = resolve_torch_device('auto')
|
|
99
123
|
import torch
|
|
@@ -121,6 +145,25 @@ def detr_for_object_detection(image: Batch[PIL.Image.Image], *, model_id: str, t
|
|
|
121
145
|
]
|
|
122
146
|
|
|
123
147
|
|
|
148
|
+
@pxt.udf
|
|
149
|
+
def detr_to_coco(image: PIL.Image.Image, detr_info: dict[str, Any]) -> dict[str, Any]:
|
|
150
|
+
bboxes, labels = detr_info['boxes'], detr_info['labels']
|
|
151
|
+
annotations = [
|
|
152
|
+
{
|
|
153
|
+
'bbox': [bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1]],
|
|
154
|
+
'category': label
|
|
155
|
+
}
|
|
156
|
+
for bbox, label in zip(bboxes, labels)
|
|
157
|
+
]
|
|
158
|
+
return {
|
|
159
|
+
'image': {
|
|
160
|
+
'width': image.width,
|
|
161
|
+
'height': image.height
|
|
162
|
+
},
|
|
163
|
+
'annotations': annotations
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
124
167
|
T = TypeVar('T')
|
|
125
168
|
|
|
126
169
|
|
pixeltable/functions/openai.py
CHANGED
|
@@ -16,8 +16,13 @@ from pixeltable import env
|
|
|
16
16
|
from pixeltable.func import Batch
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
@env.register_client('openai')
|
|
20
|
+
def _(api_key: str) -> openai.OpenAI:
|
|
21
|
+
return openai.OpenAI(api_key=api_key)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _openai_client() -> openai.OpenAI:
|
|
25
|
+
return env.Env.get().get_client('openai')
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
# Exponential backoff decorator using tenacity.
|
|
@@ -44,7 +49,7 @@ def speech(
|
|
|
44
49
|
response_format: Optional[str] = None,
|
|
45
50
|
speed: Optional[float] = None
|
|
46
51
|
) -> str:
|
|
47
|
-
content =
|
|
52
|
+
content = _openai_client().audio.speech.create(
|
|
48
53
|
input=input,
|
|
49
54
|
model=model,
|
|
50
55
|
voice=voice,
|
|
@@ -53,7 +58,7 @@ def speech(
|
|
|
53
58
|
)
|
|
54
59
|
ext = response_format or 'mp3'
|
|
55
60
|
output_filename = str(env.Env.get().tmp_dir / f"{uuid.uuid4()}.{ext}")
|
|
56
|
-
content.
|
|
61
|
+
content.write_to_file(output_filename)
|
|
57
62
|
return output_filename
|
|
58
63
|
|
|
59
64
|
|
|
@@ -71,7 +76,7 @@ def transcriptions(
|
|
|
71
76
|
temperature: Optional[float] = None
|
|
72
77
|
) -> dict:
|
|
73
78
|
file = pathlib.Path(audio)
|
|
74
|
-
transcription =
|
|
79
|
+
transcription = _openai_client().audio.transcriptions.create(
|
|
75
80
|
file=file,
|
|
76
81
|
model=model,
|
|
77
82
|
language=_opt(language),
|
|
@@ -93,7 +98,7 @@ def translations(
|
|
|
93
98
|
temperature: Optional[float] = None
|
|
94
99
|
) -> dict:
|
|
95
100
|
file = pathlib.Path(audio)
|
|
96
|
-
translation =
|
|
101
|
+
translation = _openai_client().audio.translations.create(
|
|
97
102
|
file=file,
|
|
98
103
|
model=model,
|
|
99
104
|
prompt=_opt(prompt),
|
|
@@ -127,7 +132,7 @@ def chat_completions(
|
|
|
127
132
|
tool_choice: Optional[dict] = None,
|
|
128
133
|
user: Optional[str] = None
|
|
129
134
|
) -> dict:
|
|
130
|
-
result =
|
|
135
|
+
result = _openai_client().chat.completions.create(
|
|
131
136
|
messages=messages,
|
|
132
137
|
model=model,
|
|
133
138
|
frequency_penalty=_opt(frequency_penalty),
|
|
@@ -171,7 +176,7 @@ def vision(
|
|
|
171
176
|
}}
|
|
172
177
|
]}
|
|
173
178
|
]
|
|
174
|
-
result =
|
|
179
|
+
result = _openai_client().chat.completions.create(
|
|
175
180
|
messages=messages,
|
|
176
181
|
model=model
|
|
177
182
|
)
|
|
@@ -181,17 +186,26 @@ def vision(
|
|
|
181
186
|
#####################################
|
|
182
187
|
# Embeddings Endpoints
|
|
183
188
|
|
|
189
|
+
_embedding_dimensions_cache: dict[str, int] = {
|
|
190
|
+
'text-embedding-ada-002': 1536,
|
|
191
|
+
'text-embedding-3-small': 1536,
|
|
192
|
+
'text-embedding-3-large': 3072,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
184
196
|
@pxt.udf(batch_size=32, return_type=ts.ArrayType((None,), dtype=ts.FloatType()))
|
|
185
197
|
@_retry
|
|
186
198
|
def embeddings(
|
|
187
199
|
input: Batch[str],
|
|
188
200
|
*,
|
|
189
201
|
model: str,
|
|
202
|
+
dimensions: Optional[int] = None,
|
|
190
203
|
user: Optional[str] = None
|
|
191
204
|
) -> Batch[np.ndarray]:
|
|
192
|
-
result =
|
|
205
|
+
result = _openai_client().embeddings.create(
|
|
193
206
|
input=input,
|
|
194
207
|
model=model,
|
|
208
|
+
dimensions=_opt(dimensions),
|
|
195
209
|
user=_opt(user),
|
|
196
210
|
encoding_format='float'
|
|
197
211
|
)
|
|
@@ -201,6 +215,16 @@ def embeddings(
|
|
|
201
215
|
]
|
|
202
216
|
|
|
203
217
|
|
|
218
|
+
@embeddings.conditional_return_type
|
|
219
|
+
def _(model: str, dimensions: Optional[int] = None) -> ts.ArrayType:
|
|
220
|
+
if dimensions is None:
|
|
221
|
+
if model not in _embedding_dimensions_cache:
|
|
222
|
+
# TODO: find some other way to retrieve a sample
|
|
223
|
+
return ts.ArrayType((None,), dtype=ts.FloatType(), nullable=False)
|
|
224
|
+
dimensions = _embedding_dimensions_cache.get(model, None)
|
|
225
|
+
return ts.ArrayType((dimensions,), dtype=ts.FloatType(), nullable=False)
|
|
226
|
+
|
|
227
|
+
|
|
204
228
|
#####################################
|
|
205
229
|
# Images Endpoints
|
|
206
230
|
|
|
@@ -216,7 +240,7 @@ def image_generations(
|
|
|
216
240
|
user: Optional[str] = None
|
|
217
241
|
) -> PIL.Image.Image:
|
|
218
242
|
# TODO(aaron-siegel): Decompose CPU/GPU ops into separate functions
|
|
219
|
-
result =
|
|
243
|
+
result = _openai_client().images.generate(
|
|
220
244
|
prompt=prompt,
|
|
221
245
|
model=_opt(model),
|
|
222
246
|
quality=_opt(quality),
|
|
@@ -232,6 +256,20 @@ def image_generations(
|
|
|
232
256
|
return img
|
|
233
257
|
|
|
234
258
|
|
|
259
|
+
@image_generations.conditional_return_type
|
|
260
|
+
def _(size: Optional[str] = None) -> ts.ImageType:
|
|
261
|
+
if size is None:
|
|
262
|
+
return ts.ImageType(size=(1024, 1024))
|
|
263
|
+
x_pos = size.find('x')
|
|
264
|
+
if x_pos == -1:
|
|
265
|
+
return ts.ImageType()
|
|
266
|
+
try:
|
|
267
|
+
width, height = int(size[:x_pos]), int(size[x_pos + 1:])
|
|
268
|
+
except ValueError:
|
|
269
|
+
return ts.ImageType()
|
|
270
|
+
return ts.ImageType(size=(width, height))
|
|
271
|
+
|
|
272
|
+
|
|
235
273
|
#####################################
|
|
236
274
|
# Moderations Endpoints
|
|
237
275
|
|
|
@@ -242,7 +280,7 @@ def moderations(
|
|
|
242
280
|
*,
|
|
243
281
|
model: Optional[str] = None
|
|
244
282
|
) -> dict:
|
|
245
|
-
result =
|
|
283
|
+
result = _openai_client().moderations.create(
|
|
246
284
|
input=input,
|
|
247
285
|
model=_opt(model)
|
|
248
286
|
)
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Tuple, Optional
|
|
2
2
|
|
|
3
3
|
import PIL.Image
|
|
4
|
+
from PIL.Image import Dither
|
|
4
5
|
|
|
5
|
-
from pixeltable.type_system import FloatType, ImageType, IntType, ArrayType, ColumnType, StringType, JsonType, BoolType
|
|
6
6
|
import pixeltable.func as func
|
|
7
|
+
from pixeltable.type_system import FloatType, ImageType, IntType, ArrayType, ColumnType, StringType, JsonType
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
def _caller_return_type(bound_args: Optional[Dict[str, Any]]) -> ColumnType:
|
|
10
|
-
if bound_args is None:
|
|
11
|
-
return ImageType()
|
|
12
|
-
return bound_args['self'].col_type
|
|
13
|
-
|
|
14
10
|
@func.udf(
|
|
15
11
|
py_fn=PIL.Image.alpha_composite, return_type=ImageType(), param_types=[ImageType(), ImageType()])
|
|
16
12
|
def alpha_composite(im1: PIL.Image.Image, im2: PIL.Image.Image) -> PIL.Image.Image:
|
|
@@ -28,71 +24,78 @@ def composite(image1: PIL.Image.Image, image2: PIL.Image.Image, mask: PIL.Image.
|
|
|
28
24
|
# PIL.Image.Image methods
|
|
29
25
|
|
|
30
26
|
# Image.convert()
|
|
31
|
-
|
|
32
|
-
if bound_args is None:
|
|
33
|
-
return ImageType()
|
|
34
|
-
assert 'self' in bound_args
|
|
35
|
-
assert 'mode' in bound_args
|
|
36
|
-
img_type = bound_args['self'].col_type
|
|
37
|
-
return ImageType(size=img_type.size, mode=bound_args['mode'])
|
|
38
|
-
@func.udf(return_type=_convert_return_type, param_types=[ImageType(), StringType()])
|
|
27
|
+
@func.udf(param_types=[ImageType(), StringType()])
|
|
39
28
|
def convert(self: PIL.Image.Image, mode: str) -> PIL.Image.Image:
|
|
40
29
|
return self.convert(mode)
|
|
41
30
|
|
|
31
|
+
|
|
32
|
+
@convert.conditional_return_type
|
|
33
|
+
def _(self: PIL.Image.Image, mode: str) -> ColumnType:
|
|
34
|
+
input_type = self.col_type
|
|
35
|
+
assert input_type.is_image_type()
|
|
36
|
+
return ImageType(size=input_type.size, mode=mode, nullable=input_type.nullable)
|
|
37
|
+
|
|
38
|
+
|
|
42
39
|
# Image.crop()
|
|
43
|
-
def _crop_return_type(bound_args: Dict[str, Any]) -> ColumnType:
|
|
44
|
-
if bound_args is None:
|
|
45
|
-
return ImageType()
|
|
46
|
-
img_type = bound_args['self'].col_type
|
|
47
|
-
box = bound_args['box']
|
|
48
|
-
if isinstance(box, list) and all(isinstance(x, int) for x in box):
|
|
49
|
-
return ImageType(size=(box[2] - box[0], box[3] - box[1]), mode=img_type.mode)
|
|
50
|
-
return ImageType() # we can't compute the size statically
|
|
51
40
|
@func.udf(
|
|
52
|
-
py_fn=PIL.Image.Image.crop,
|
|
41
|
+
py_fn=PIL.Image.Image.crop,
|
|
53
42
|
param_types=[ImageType(), ArrayType((4,), dtype=IntType())])
|
|
54
43
|
def crop(self: PIL.Image.Image, box: Tuple[int, int, int, int]) -> PIL.Image.Image:
|
|
55
44
|
pass
|
|
56
45
|
|
|
46
|
+
@crop.conditional_return_type
|
|
47
|
+
def _(self: PIL.Image.Image, box: Tuple[int, int, int, int]) -> ColumnType:
|
|
48
|
+
input_type = self.col_type
|
|
49
|
+
assert input_type.is_image_type()
|
|
50
|
+
if isinstance(box, list) and all(isinstance(x, int) for x in box):
|
|
51
|
+
return ImageType(size=(box[2] - box[0], box[3] - box[1]), mode=input_type.mode, nullable=input_type.nullable)
|
|
52
|
+
return ImageType(mode=input_type.mode, nullable=input_type.nullable) # we can't compute the size statically
|
|
53
|
+
|
|
57
54
|
# Image.getchannel()
|
|
58
|
-
|
|
59
|
-
if bound_args is None:
|
|
60
|
-
return ImageType()
|
|
61
|
-
img_type = bound_args['self'].col_type
|
|
62
|
-
return ImageType(size=img_type.size, mode='L')
|
|
63
|
-
@func.udf(
|
|
64
|
-
py_fn=PIL.Image.Image.getchannel, return_type=_getchannel_return_type, param_types=[ImageType(), IntType()])
|
|
55
|
+
@func.udf(py_fn=PIL.Image.Image.getchannel, param_types=[ImageType(), IntType()])
|
|
65
56
|
def getchannel(self: PIL.Image.Image, channel: int) -> PIL.Image.Image:
|
|
66
57
|
pass
|
|
67
58
|
|
|
59
|
+
@getchannel.conditional_return_type
|
|
60
|
+
def _(self: PIL.Image.Image) -> ColumnType:
|
|
61
|
+
input_type = self.col_type
|
|
62
|
+
assert input_type.is_image_type()
|
|
63
|
+
return ImageType(size=input_type.size, mode='L', nullable=input_type.nullable)
|
|
64
|
+
|
|
65
|
+
|
|
68
66
|
# Image.resize()
|
|
69
|
-
|
|
70
|
-
if bound_args is None:
|
|
71
|
-
return ImageType()
|
|
72
|
-
assert 'size' in bound_args
|
|
73
|
-
return ImageType(size=bound_args['size'])
|
|
74
|
-
@func.udf(return_type=resize_return_type, param_types=[ImageType(), ArrayType((2, ), dtype=IntType())])
|
|
67
|
+
@func.udf(param_types=[ImageType(), ArrayType((2, ), dtype=IntType())])
|
|
75
68
|
def resize(self: PIL.Image.Image, size: Tuple[int, int]) -> PIL.Image.Image:
|
|
76
69
|
return self.resize(size)
|
|
77
70
|
|
|
71
|
+
@resize.conditional_return_type
|
|
72
|
+
def _(self: PIL.Image.Image, size: Tuple[int, int]) -> ColumnType:
|
|
73
|
+
input_type = self.col_type
|
|
74
|
+
assert input_type.is_image_type()
|
|
75
|
+
return ImageType(size=size, mode=input_type.mode, nullable=input_type.nullable)
|
|
76
|
+
|
|
78
77
|
# Image.rotate()
|
|
79
|
-
@func.udf(
|
|
78
|
+
@func.udf(param_types=[ImageType(), IntType()])
|
|
80
79
|
def rotate(self: PIL.Image.Image, angle: int) -> PIL.Image.Image:
|
|
81
80
|
return self.rotate(angle)
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return self.transform(size, method)
|
|
82
|
+
@func.udf(py_fn=PIL.Image.Image.effect_spread, param_types=[ImageType(), IntType()])
|
|
83
|
+
def effect_spread(self: PIL.Image.Image, distance: int) -> PIL.Image.Image:
|
|
84
|
+
pass
|
|
87
85
|
|
|
88
|
-
@func.udf(
|
|
89
|
-
|
|
90
|
-
def effect_spread(self: PIL.Image.Image, distance: float) -> PIL.Image.Image:
|
|
86
|
+
@func.udf(py_fn=PIL.Image.Image.transpose, param_types=[ImageType(), IntType()])
|
|
87
|
+
def transpose(self: PIL.Image.Image, method: int) -> PIL.Image.Image:
|
|
91
88
|
pass
|
|
92
89
|
|
|
90
|
+
@rotate.conditional_return_type
|
|
91
|
+
@effect_spread.conditional_return_type
|
|
92
|
+
@transpose.conditional_return_type
|
|
93
|
+
def _(self: PIL.Image.Image) -> ColumnType:
|
|
94
|
+
return self.col_type
|
|
95
|
+
|
|
93
96
|
@func.udf(
|
|
94
97
|
py_fn=PIL.Image.Image.entropy, return_type=FloatType(), param_types=[ImageType(), ImageType(), JsonType()])
|
|
95
|
-
def entropy(self: PIL.Image.Image, mask: PIL.Image.Image,
|
|
98
|
+
def entropy(self: PIL.Image.Image, mask: PIL.Image.Image, extrema: Optional[list] = None) -> float:
|
|
96
99
|
pass
|
|
97
100
|
|
|
98
101
|
@func.udf(py_fn=PIL.Image.Image.getbands, return_type=JsonType(), param_types=[ImageType()])
|
|
@@ -103,8 +106,7 @@ def getbands(self: PIL.Image.Image) -> Tuple[str]:
|
|
|
103
106
|
def getbbox(self: PIL.Image.Image) -> Tuple[int, int, int, int]:
|
|
104
107
|
pass
|
|
105
108
|
|
|
106
|
-
@func.udf(
|
|
107
|
-
py_fn=PIL.Image.Image.getcolors, return_type=JsonType(), param_types=[ImageType(), IntType()])
|
|
109
|
+
@func.udf(py_fn=PIL.Image.Image.getcolors, return_type=JsonType(), param_types=[ImageType(), IntType()])
|
|
108
110
|
def getcolors(self: PIL.Image.Image, maxcolors: int) -> Tuple[Tuple[int, int, int], int]:
|
|
109
111
|
pass
|
|
110
112
|
|
|
@@ -114,37 +116,32 @@ def getextrema(self: PIL.Image.Image) -> Tuple[int, int]:
|
|
|
114
116
|
|
|
115
117
|
@func.udf(
|
|
116
118
|
py_fn=PIL.Image.Image.getpalette, return_type=JsonType(), param_types=[ImageType(), StringType()])
|
|
117
|
-
def getpalette(self: PIL.Image.Image, mode: str) -> Tuple[int]:
|
|
119
|
+
def getpalette(self: PIL.Image.Image, mode: Optional[str] = None) -> Tuple[int]:
|
|
118
120
|
pass
|
|
119
121
|
|
|
120
122
|
@func.udf(
|
|
121
|
-
|
|
122
|
-
def getpixel(self: PIL.Image.Image, xy:
|
|
123
|
-
|
|
123
|
+
return_type=JsonType(), param_types=[ImageType(), ArrayType((2,), dtype=IntType())])
|
|
124
|
+
def getpixel(self: PIL.Image.Image, xy: tuple[int, int]) -> Tuple[int]:
|
|
125
|
+
# `xy` will be a list; `tuple(xy)` is necessary for pillow 9 compatibility
|
|
126
|
+
return self.getpixel(tuple(xy))
|
|
124
127
|
|
|
125
|
-
@func.udf(
|
|
126
|
-
py_fn=PIL.Image.Image.getprojection, return_type=JsonType(), param_types=[ImageType()])
|
|
128
|
+
@func.udf(py_fn=PIL.Image.Image.getprojection, return_type=JsonType(), param_types=[ImageType()])
|
|
127
129
|
def getprojection(self: PIL.Image.Image) -> Tuple[int]:
|
|
128
130
|
pass
|
|
129
131
|
|
|
130
|
-
@func.udf(
|
|
131
|
-
|
|
132
|
-
def histogram(self: PIL.Image.Image, mask: PIL.Image.Image, histogram: Dict) -> Tuple[int]:
|
|
132
|
+
@func.udf(py_fn=PIL.Image.Image.histogram, return_type=JsonType(), param_types=[ImageType(), ImageType(), JsonType()])
|
|
133
|
+
def histogram(self: PIL.Image.Image, mask: PIL.Image.Image, extrema: Optional[list] = None) -> Tuple[int]:
|
|
133
134
|
pass
|
|
134
135
|
|
|
135
136
|
@func.udf(
|
|
136
137
|
py_fn=PIL.Image.Image.quantize, return_type=ImageType(),
|
|
137
138
|
param_types=[ImageType(), IntType(), IntType(nullable=True), IntType(), IntType(nullable=True), IntType()])
|
|
138
139
|
def quantize(
|
|
139
|
-
self: PIL.Image.Image, colors: int
|
|
140
|
+
self: PIL.Image.Image, colors: int = 256, method: Optional[int] = None, kmeans: int = 0,
|
|
141
|
+
palette: Optional[int] = None, dither: int = Dither.FLOYDSTEINBERG) -> PIL.Image.Image:
|
|
140
142
|
pass
|
|
141
143
|
|
|
142
144
|
@func.udf(
|
|
143
145
|
py_fn=PIL.Image.Image.reduce, return_type=ImageType(), param_types=[ImageType(), IntType(), JsonType()])
|
|
144
|
-
def reduce(self: PIL.Image.Image, factor: int,
|
|
145
|
-
pass
|
|
146
|
-
|
|
147
|
-
@func.udf(
|
|
148
|
-
py_fn=PIL.Image.Image.transpose, return_type=_caller_return_type, param_types=[ImageType(), IntType()])
|
|
149
|
-
def transpose(self: PIL.Image.Image, method: int) -> PIL.Image.Image:
|
|
146
|
+
def reduce(self: PIL.Image.Image, factor: int, box: Optional[Tuple[int]]) -> PIL.Image.Image:
|
|
150
147
|
pass
|
pixeltable/functions/together.py
CHANGED
|
@@ -11,8 +11,13 @@ from pixeltable import env
|
|
|
11
11
|
from pixeltable.func import Batch
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
@env.register_client('together')
|
|
15
|
+
def _(api_key: str) -> together.Together:
|
|
16
|
+
return together.Together(api_key=api_key)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _together_client() -> together.Together:
|
|
20
|
+
return env.Env.get().get_client('together')
|
|
16
21
|
|
|
17
22
|
|
|
18
23
|
@pxt.udf
|
|
@@ -31,7 +36,7 @@ def completions(
|
|
|
31
36
|
n: Optional[int] = None,
|
|
32
37
|
safety_model: Optional[str] = None
|
|
33
38
|
) -> dict:
|
|
34
|
-
return
|
|
39
|
+
return _together_client().completions.create(
|
|
35
40
|
prompt=prompt,
|
|
36
41
|
model=model,
|
|
37
42
|
max_tokens=max_tokens,
|
|
@@ -66,7 +71,7 @@ def chat_completions(
|
|
|
66
71
|
tools: Optional[dict] = None,
|
|
67
72
|
tool_choice: Optional[dict] = None
|
|
68
73
|
) -> dict:
|
|
69
|
-
return
|
|
74
|
+
return _together_client().chat.completions.create(
|
|
70
75
|
messages=messages,
|
|
71
76
|
model=model,
|
|
72
77
|
max_tokens=max_tokens,
|
|
@@ -85,15 +90,36 @@ def chat_completions(
|
|
|
85
90
|
).dict()
|
|
86
91
|
|
|
87
92
|
|
|
93
|
+
_embedding_dimensions_cache = {
|
|
94
|
+
'togethercomputer/m2-bert-80M-2k-retrieval': 768,
|
|
95
|
+
'togethercomputer/m2-bert-80M-8k-retrieval': 768,
|
|
96
|
+
'togethercomputer/m2-bert-80M-32k-retrieval': 768,
|
|
97
|
+
'WhereIsAI/UAE-Large-V1': 1024,
|
|
98
|
+
'BAAI/bge-large-en-v1.5': 1024,
|
|
99
|
+
'BAAI/bge-base-en-v1.5': 768,
|
|
100
|
+
'sentence-transformers/msmarco-bert-base-dot-v5': 768,
|
|
101
|
+
'bert-base-uncased': 768,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
88
105
|
@pxt.udf(batch_size=32, return_type=pxt.ArrayType((None,), dtype=pxt.FloatType()))
|
|
89
106
|
def embeddings(input: Batch[str], *, model: str) -> Batch[np.ndarray]:
|
|
90
|
-
result =
|
|
107
|
+
result = _together_client().embeddings.create(input=input, model=model)
|
|
91
108
|
return [
|
|
92
109
|
np.array(data.embedding, dtype=np.float64)
|
|
93
110
|
for data in result.data
|
|
94
111
|
]
|
|
95
112
|
|
|
96
113
|
|
|
114
|
+
@embeddings.conditional_return_type
|
|
115
|
+
def _(model: str) -> pxt.ArrayType:
|
|
116
|
+
if model not in _embedding_dimensions_cache:
|
|
117
|
+
# TODO: find some other way to retrieve a sample
|
|
118
|
+
return pxt.ArrayType((None,), dtype=pxt.FloatType())
|
|
119
|
+
dimensions = _embedding_dimensions_cache[model]
|
|
120
|
+
return pxt.ArrayType((dimensions,), dtype=pxt.FloatType())
|
|
121
|
+
|
|
122
|
+
|
|
97
123
|
@pxt.udf
|
|
98
124
|
def image_generations(
|
|
99
125
|
prompt: str,
|
|
@@ -106,7 +132,7 @@ def image_generations(
|
|
|
106
132
|
negative_prompt: Optional[str] = None,
|
|
107
133
|
) -> PIL.Image.Image:
|
|
108
134
|
# TODO(aaron-siegel): Decompose CPU/GPU ops into separate functions
|
|
109
|
-
result =
|
|
135
|
+
result = _together_client().images.generate(
|
|
110
136
|
prompt=prompt,
|
|
111
137
|
model=model,
|
|
112
138
|
steps=steps,
|
pixeltable/functions/util.py
CHANGED
|
@@ -1,46 +1,3 @@
|
|
|
1
|
-
from typing import Tuple, List, Optional
|
|
2
|
-
import types
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
|
-
import pixeltable.func as func
|
|
6
|
-
import pixeltable.type_system as ts
|
|
7
|
-
import pixeltable.env as env
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def create_nos_modules() -> List[types.ModuleType]:
|
|
11
|
-
"""Create module pixeltable.functions.nos with one submodule per task and return the submodules"""
|
|
12
|
-
models = env.Env.get().nos_client.ListModels()
|
|
13
|
-
model_info = [env.Env.get().nos_client.GetModelInfo(model) for model in models]
|
|
14
|
-
model_info.sort(key=lambda info: info.task.value)
|
|
15
|
-
|
|
16
|
-
module_name = 'pixeltable.functions.nos'
|
|
17
|
-
nos_module = types.ModuleType(module_name)
|
|
18
|
-
nos_module.__package__ = 'pixeltable.functions'
|
|
19
|
-
sys.modules[module_name] = nos_module
|
|
20
|
-
|
|
21
|
-
prev_task = ''
|
|
22
|
-
new_modules: List[types.ModuleType] = []
|
|
23
|
-
sub_module: Optional[types.ModuleType] = None
|
|
24
|
-
for info in model_info:
|
|
25
|
-
if info.task.value != prev_task:
|
|
26
|
-
# we construct one submodule per task
|
|
27
|
-
namespace = info.task.name.lower()
|
|
28
|
-
submodule_name = f'{module_name}.{namespace}'
|
|
29
|
-
sub_module = types.ModuleType(submodule_name)
|
|
30
|
-
sub_module.__package__ = module_name
|
|
31
|
-
setattr(nos_module, namespace, sub_module)
|
|
32
|
-
new_modules.append(sub_module)
|
|
33
|
-
sys.modules[submodule_name] = sub_module
|
|
34
|
-
prev_task = info.task.value
|
|
35
|
-
|
|
36
|
-
# add a Function for this model to the module
|
|
37
|
-
model_id = info.name.replace("/", "_").replace("-", "_")
|
|
38
|
-
pt_func = func.NOSFunction(info, f'{submodule_name}.{model_id}')
|
|
39
|
-
setattr(sub_module, model_id, pt_func)
|
|
40
|
-
|
|
41
|
-
return new_modules
|
|
42
|
-
|
|
43
|
-
|
|
44
1
|
def resolve_torch_device(device: str) -> str:
|
|
45
2
|
import torch
|
|
46
3
|
if device == 'auto':
|
pixeltable/functions/video.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
1
|
import uuid
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
3
4
|
import av
|
|
4
|
-
import sys
|
|
5
5
|
|
|
6
6
|
import pixeltable.env as env
|
|
7
7
|
import pixeltable.func as func
|
|
8
8
|
import pixeltable.type_system as ts
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
_format_defaults = { # format -> (codec, ext)
|
|
10
|
+
_format_defaults = { # format -> (codec, ext)
|
|
12
11
|
'wav': ('pcm_s16le', 'wav'),
|
|
13
12
|
'mp3': ('libmp3lame', 'mp3'),
|
|
14
13
|
'flac': ('flac', 'flac'),
|
|
@@ -35,11 +34,13 @@ _extract_audio_param_types = [
|
|
|
35
34
|
ts.VideoType(nullable=False),
|
|
36
35
|
ts.IntType(nullable=False),
|
|
37
36
|
ts.StringType(nullable=False),
|
|
38
|
-
ts.StringType(nullable=
|
|
37
|
+
ts.StringType(nullable=True),
|
|
39
38
|
]
|
|
39
|
+
|
|
40
|
+
|
|
40
41
|
@func.udf(return_type=ts.AudioType(nullable=True), param_types=_extract_audio_param_types)
|
|
41
42
|
def extract_audio(
|
|
42
|
-
|
|
43
|
+
video_path: str, stream_idx: int = 0, format: str = 'wav', codec: Optional[str] = None
|
|
43
44
|
) -> Optional[str]:
|
|
44
45
|
"""Extract an audio stream from a video file, save it as a media file and return its path"""
|
|
45
46
|
if format not in _format_defaults:
|
|
@@ -51,12 +52,49 @@ def extract_audio(
|
|
|
51
52
|
return None
|
|
52
53
|
audio_stream = container.streams.audio[stream_idx]
|
|
53
54
|
# create this in our tmp directory, so it'll get cleaned up if it's being generated as part of a query
|
|
54
|
-
output_filename = str(env.Env.get().tmp_dir / f
|
|
55
|
+
output_filename = str(env.Env.get().tmp_dir / f'{uuid.uuid4()}.{ext}')
|
|
55
56
|
|
|
56
|
-
with av.open(output_filename,
|
|
57
|
+
with av.open(output_filename, 'w', format=format) as output_container:
|
|
57
58
|
output_stream = output_container.add_stream(codec or default_codec)
|
|
58
59
|
for packet in container.demux(audio_stream):
|
|
59
60
|
for frame in packet.decode():
|
|
60
61
|
output_container.mux(output_stream.encode(frame))
|
|
61
62
|
|
|
62
63
|
return output_filename
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@func.udf(return_type=ts.JsonType(nullable=False), param_types=[ts.VideoType(nullable=False)])
|
|
67
|
+
def get_metadata(video: str) -> dict:
|
|
68
|
+
"""Gets various metadata associated with a video file.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
video (str): Path to the video file.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
A dictionary containing the associated metadata.
|
|
75
|
+
"""
|
|
76
|
+
with av.open(video) as container:
|
|
77
|
+
assert isinstance(container, av.container.InputContainer)
|
|
78
|
+
video_streams_info = [
|
|
79
|
+
{
|
|
80
|
+
'duration': stream.duration,
|
|
81
|
+
'frames': stream.frames,
|
|
82
|
+
'language': stream.language,
|
|
83
|
+
'average_rate': float(stream.average_rate) if stream.average_rate is not None else None,
|
|
84
|
+
'base_rate': float(stream.base_rate) if stream.base_rate is not None else None,
|
|
85
|
+
'guessed_rate': float(stream.guessed_rate) if stream.guessed_rate is not None else None,
|
|
86
|
+
'pix_fmt': getattr(stream.codec_context, 'pix_fmt', None),
|
|
87
|
+
'width': stream.width,
|
|
88
|
+
'height': stream.height,
|
|
89
|
+
}
|
|
90
|
+
for stream in container.streams
|
|
91
|
+
if isinstance(stream, av.video.stream.VideoStream)
|
|
92
|
+
]
|
|
93
|
+
result = {
|
|
94
|
+
'bit_exact': container.bit_exact,
|
|
95
|
+
'bit_rate': container.bit_rate,
|
|
96
|
+
'size': container.size,
|
|
97
|
+
'metadata': container.metadata,
|
|
98
|
+
'streams': video_streams_info, # TODO: Audio streams?
|
|
99
|
+
}
|
|
100
|
+
return result
|