pixeltable 0.2.28__py3-none-any.whl → 0.2.29__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/__version__.py +2 -2
- pixeltable/catalog/__init__.py +1 -1
- pixeltable/catalog/dir.py +6 -0
- pixeltable/catalog/globals.py +13 -0
- pixeltable/catalog/named_function.py +4 -0
- pixeltable/catalog/path_dict.py +37 -11
- pixeltable/catalog/schema_object.py +6 -0
- pixeltable/catalog/table.py +22 -5
- pixeltable/catalog/table_version.py +22 -8
- pixeltable/dataframe.py +201 -3
- pixeltable/env.py +9 -3
- pixeltable/exec/expr_eval_node.py +1 -1
- pixeltable/exec/sql_node.py +2 -2
- pixeltable/exprs/function_call.py +134 -24
- pixeltable/exprs/inline_expr.py +22 -2
- pixeltable/exprs/row_builder.py +1 -1
- pixeltable/exprs/similarity_expr.py +9 -2
- pixeltable/func/aggregate_function.py +148 -68
- pixeltable/func/callable_function.py +49 -13
- pixeltable/func/expr_template_function.py +55 -24
- pixeltable/func/function.py +183 -22
- pixeltable/func/function_registry.py +2 -1
- pixeltable/func/query_template_function.py +11 -6
- pixeltable/func/signature.py +64 -7
- pixeltable/func/udf.py +57 -35
- pixeltable/functions/globals.py +54 -34
- pixeltable/functions/json.py +3 -8
- pixeltable/functions/ollama.py +4 -4
- pixeltable/functions/timestamp.py +1 -1
- pixeltable/functions/video.py +2 -8
- pixeltable/functions/vision.py +1 -1
- pixeltable/globals.py +218 -59
- pixeltable/index/embedding_index.py +44 -24
- pixeltable/metadata/__init__.py +1 -1
- pixeltable/metadata/converters/convert_16.py +2 -1
- pixeltable/metadata/converters/convert_17.py +2 -1
- pixeltable/metadata/converters/convert_23.py +35 -0
- pixeltable/metadata/converters/convert_24.py +47 -0
- pixeltable/metadata/converters/util.py +4 -2
- pixeltable/metadata/notes.py +2 -0
- pixeltable/metadata/schema.py +1 -0
- pixeltable/tool/create_test_db_dump.py +11 -0
- pixeltable/tool/doc_plugins/griffe.py +4 -3
- pixeltable/type_system.py +180 -45
- {pixeltable-0.2.28.dist-info → pixeltable-0.2.29.dist-info}/METADATA +3 -2
- {pixeltable-0.2.28.dist-info → pixeltable-0.2.29.dist-info}/RECORD +49 -47
- {pixeltable-0.2.28.dist-info → pixeltable-0.2.29.dist-info}/LICENSE +0 -0
- {pixeltable-0.2.28.dist-info → pixeltable-0.2.29.dist-info}/WHEEL +0 -0
- {pixeltable-0.2.28.dist-info → pixeltable-0.2.29.dist-info}/entry_points.txt +0 -0
pixeltable/func/signature.py
CHANGED
|
@@ -111,6 +111,38 @@ class Signature:
|
|
|
111
111
|
parameters = [Parameter.from_dict(param_dict) for param_dict in d['parameters']]
|
|
112
112
|
return cls(ts.ColumnType.from_dict(d['return_type']), parameters, d['is_batched'])
|
|
113
113
|
|
|
114
|
+
def is_consistent_with(self, other: Signature) -> bool:
|
|
115
|
+
"""
|
|
116
|
+
Returns True if this signature is consistent with the other signature.
|
|
117
|
+
S is consistent with T if we could safely replace S by T in any call where S is used. Specifically:
|
|
118
|
+
(i) S.return_type is a supertype of T.return_type
|
|
119
|
+
(ii) For each parameter p in S, there is a parameter q in T such that:
|
|
120
|
+
- p and q have the same name and kind
|
|
121
|
+
- q.col_type is a supertype of p.col_type
|
|
122
|
+
(iii) For each *required* parameter q in T, there is a parameter p in S with the same name (in which
|
|
123
|
+
case the kinds and types must also match, by condition (ii)).
|
|
124
|
+
"""
|
|
125
|
+
# Check (i)
|
|
126
|
+
if not self.get_return_type().is_supertype_of(other.get_return_type(), ignore_nullable=True):
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
# Check (ii)
|
|
130
|
+
for param_name, param in self.parameters.items():
|
|
131
|
+
if param_name not in other.parameters:
|
|
132
|
+
return False
|
|
133
|
+
other_param = other.parameters[param_name]
|
|
134
|
+
if (param.kind != other_param.kind or
|
|
135
|
+
(param.col_type is None) != (other_param.col_type is None) or # this can happen if they are varargs
|
|
136
|
+
param.col_type is not None and not other_param.col_type.is_supertype_of(param.col_type, ignore_nullable=True)):
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
# Check (iii)
|
|
140
|
+
for other_param in other.required_parameters:
|
|
141
|
+
if other_param.name not in self.parameters:
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
return True
|
|
145
|
+
|
|
114
146
|
def __eq__(self, other: object) -> bool:
|
|
115
147
|
if not isinstance(other, Signature):
|
|
116
148
|
return False
|
|
@@ -156,8 +188,12 @@ class Signature:
|
|
|
156
188
|
|
|
157
189
|
@classmethod
|
|
158
190
|
def create_parameters(
|
|
159
|
-
|
|
160
|
-
|
|
191
|
+
cls,
|
|
192
|
+
py_fn: Optional[Callable] = None,
|
|
193
|
+
py_params: Optional[list[inspect.Parameter]] = None,
|
|
194
|
+
param_types: Optional[list[ts.ColumnType]] = None,
|
|
195
|
+
type_substitutions: Optional[dict] = None,
|
|
196
|
+
is_cls_method: bool = False
|
|
161
197
|
) -> list[Parameter]:
|
|
162
198
|
assert (py_fn is None) != (py_params is None)
|
|
163
199
|
if py_fn is not None:
|
|
@@ -165,7 +201,12 @@ class Signature:
|
|
|
165
201
|
py_params = list(sig.parameters.values())
|
|
166
202
|
parameters: list[Parameter] = []
|
|
167
203
|
|
|
204
|
+
if type_substitutions is None:
|
|
205
|
+
type_substitutions = {}
|
|
206
|
+
|
|
168
207
|
for idx, param in enumerate(py_params):
|
|
208
|
+
if is_cls_method and idx == 0:
|
|
209
|
+
continue # skip 'self' or 'cls' parameter
|
|
169
210
|
if param.name in cls.SPECIAL_PARAM_NAMES:
|
|
170
211
|
raise excs.Error(f"'{param.name}' is a reserved parameter name")
|
|
171
212
|
if param.kind == inspect.Parameter.VAR_POSITIONAL or param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
@@ -179,7 +220,12 @@ class Signature:
|
|
|
179
220
|
param_type = param_types[idx]
|
|
180
221
|
is_batched = False
|
|
181
222
|
else:
|
|
182
|
-
|
|
223
|
+
py_type: Optional[type]
|
|
224
|
+
if param.annotation in type_substitutions:
|
|
225
|
+
py_type = type_substitutions[param.annotation]
|
|
226
|
+
else:
|
|
227
|
+
py_type = param.annotation
|
|
228
|
+
param_type, is_batched = cls._infer_type(py_type)
|
|
183
229
|
if param_type is None:
|
|
184
230
|
raise excs.Error(f'Cannot infer pixeltable type for parameter {param.name}')
|
|
185
231
|
|
|
@@ -190,18 +236,29 @@ class Signature:
|
|
|
190
236
|
|
|
191
237
|
@classmethod
|
|
192
238
|
def create(
|
|
193
|
-
cls,
|
|
239
|
+
cls,
|
|
240
|
+
py_fn: Callable,
|
|
194
241
|
param_types: Optional[list[ts.ColumnType]] = None,
|
|
195
|
-
return_type: Optional[ts.ColumnType] = None
|
|
242
|
+
return_type: Optional[ts.ColumnType] = None,
|
|
243
|
+
type_substitutions: Optional[dict] = None,
|
|
244
|
+
is_cls_method: bool = False
|
|
196
245
|
) -> Signature:
|
|
197
246
|
"""Create a signature for the given Callable.
|
|
198
247
|
Infer the parameter and return types, if none are specified.
|
|
199
248
|
Raises an exception if the types cannot be inferred.
|
|
200
249
|
"""
|
|
201
|
-
|
|
250
|
+
if type_substitutions is None:
|
|
251
|
+
type_substitutions = {}
|
|
252
|
+
|
|
253
|
+
parameters = cls.create_parameters(py_fn=py_fn, param_types=param_types, is_cls_method=is_cls_method, type_substitutions=type_substitutions)
|
|
202
254
|
sig = inspect.signature(py_fn)
|
|
203
255
|
if return_type is None:
|
|
204
|
-
|
|
256
|
+
py_type: Optional[type]
|
|
257
|
+
if sig.return_annotation in type_substitutions:
|
|
258
|
+
py_type = type_substitutions[sig.return_annotation]
|
|
259
|
+
else:
|
|
260
|
+
py_type = sig.return_annotation
|
|
261
|
+
return_type, return_is_batched = cls._infer_type(py_type)
|
|
205
262
|
if return_type is None:
|
|
206
263
|
raise excs.Error('Cannot infer pixeltable return type')
|
|
207
264
|
else:
|
pixeltable/func/udf.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any, Callable, Optional, overload
|
|
3
|
+
from typing import Any, Callable, Optional, Sequence, overload
|
|
4
4
|
|
|
5
5
|
import pixeltable.exceptions as excs
|
|
6
6
|
import pixeltable.type_system as ts
|
|
7
7
|
|
|
8
8
|
from .callable_function import CallableFunction
|
|
9
|
-
from .expr_template_function import ExprTemplateFunction
|
|
9
|
+
from .expr_template_function import ExprTemplateFunction, ExprTemplate
|
|
10
10
|
from .function import Function
|
|
11
11
|
from .function_registry import FunctionRegistry
|
|
12
12
|
from .globals import validate_symbol_path
|
|
@@ -21,13 +21,14 @@ def udf(decorated_fn: Callable) -> Function: ...
|
|
|
21
21
|
# Decorator schema invoked with parentheses: @pxt.udf(**kwargs)
|
|
22
22
|
@overload
|
|
23
23
|
def udf(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
*,
|
|
25
|
+
batch_size: Optional[int] = None,
|
|
26
|
+
substitute_fn: Optional[Callable] = None,
|
|
27
|
+
is_method: bool = False,
|
|
28
|
+
is_property: bool = False,
|
|
29
|
+
type_substitutions: Optional[Sequence[dict]] = None,
|
|
30
|
+
_force_stored: bool = False
|
|
31
|
+
) -> Callable[[Callable], CallableFunction]: ...
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
def udf(*args, **kwargs):
|
|
@@ -52,6 +53,7 @@ def udf(*args, **kwargs):
|
|
|
52
53
|
substitute_fn = kwargs.pop('substitute_fn', None)
|
|
53
54
|
is_method = kwargs.pop('is_method', None)
|
|
54
55
|
is_property = kwargs.pop('is_property', None)
|
|
56
|
+
type_substitutions = kwargs.pop('type_substitutions', None)
|
|
55
57
|
force_stored = kwargs.pop('_force_stored', False)
|
|
56
58
|
if len(kwargs) > 0:
|
|
57
59
|
raise excs.Error(f'Invalid @udf decorator kwargs: {", ".join(kwargs.keys())}')
|
|
@@ -65,6 +67,7 @@ def udf(*args, **kwargs):
|
|
|
65
67
|
substitute_fn=substitute_fn,
|
|
66
68
|
is_method=is_method,
|
|
67
69
|
is_property=is_property,
|
|
70
|
+
type_substitutions=type_substitutions,
|
|
68
71
|
force_stored=force_stored
|
|
69
72
|
)
|
|
70
73
|
|
|
@@ -79,9 +82,10 @@ def make_function(
|
|
|
79
82
|
substitute_fn: Optional[Callable] = None,
|
|
80
83
|
is_method: bool = False,
|
|
81
84
|
is_property: bool = False,
|
|
85
|
+
type_substitutions: Optional[Sequence[dict]] = None,
|
|
82
86
|
function_name: Optional[str] = None,
|
|
83
87
|
force_stored: bool = False
|
|
84
|
-
) ->
|
|
88
|
+
) -> CallableFunction:
|
|
85
89
|
"""
|
|
86
90
|
Constructs a `CallableFunction` from the specified parameters.
|
|
87
91
|
If `substitute_fn` is specified, then `decorated_fn`
|
|
@@ -104,25 +108,43 @@ def make_function(
|
|
|
104
108
|
# Display name to use for error messages
|
|
105
109
|
errmsg_name = function_name if function_path is None else function_path
|
|
106
110
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
111
|
+
signatures: list[Signature]
|
|
112
|
+
if type_substitutions is None:
|
|
113
|
+
sig = Signature.create(decorated_fn, param_types, return_type)
|
|
114
|
+
|
|
115
|
+
# batched functions must have a batched return type
|
|
116
|
+
# TODO: remove 'Python' from the error messages when we have full inference with Annotated types
|
|
117
|
+
if batch_size is not None and not sig.is_batched:
|
|
118
|
+
raise excs.Error(f'{errmsg_name}(): batch_size is specified; Python return type must be a `Batch`')
|
|
119
|
+
if batch_size is not None and len(sig.batched_parameters) == 0:
|
|
120
|
+
raise excs.Error(f'{errmsg_name}(): batch_size is specified; at least one Python parameter must be `Batch`')
|
|
121
|
+
if batch_size is None and len(sig.batched_parameters) > 0:
|
|
122
|
+
raise excs.Error(f'{errmsg_name}(): batched parameters in udf, but no `batch_size` given')
|
|
123
|
+
|
|
124
|
+
if is_method and is_property:
|
|
125
|
+
raise excs.Error(f'Cannot specify both `is_method` and `is_property` (in function `{function_name}`)')
|
|
126
|
+
if is_property and len(sig.parameters) != 1:
|
|
127
|
+
raise excs.Error(
|
|
128
|
+
f"`is_property=True` expects a UDF with exactly 1 parameter, but `{function_name}` has {len(sig.parameters)}"
|
|
129
|
+
)
|
|
130
|
+
if (is_method or is_property) and function_path is None:
|
|
131
|
+
raise excs.Error('Stored functions cannot be declared using `is_method` or `is_property`')
|
|
132
|
+
|
|
133
|
+
signatures = [sig]
|
|
134
|
+
else:
|
|
135
|
+
if function_path is None:
|
|
136
|
+
raise excs.Error(
|
|
137
|
+
f'{errmsg_name}(): type substitutions can only be used with module UDFs (not locally defined UDFs)'
|
|
138
|
+
)
|
|
139
|
+
if batch_size is not None:
|
|
140
|
+
raise excs.Error(f'{errmsg_name}(): type substitutions cannot be used with batched functions')
|
|
141
|
+
if is_method is not None or is_property is not None:
|
|
142
|
+
# TODO: Support this for `is_method`?
|
|
143
|
+
raise excs.Error(f'{errmsg_name}(): type substitutions cannot be used with `is_method` or `is_property`')
|
|
144
|
+
signatures = [
|
|
145
|
+
Signature.create(decorated_fn, param_types, return_type, type_substitutions=subst)
|
|
146
|
+
for subst in type_substitutions
|
|
147
|
+
]
|
|
126
148
|
|
|
127
149
|
if substitute_fn is None:
|
|
128
150
|
py_fn = decorated_fn
|
|
@@ -132,8 +154,8 @@ def make_function(
|
|
|
132
154
|
py_fn = substitute_fn
|
|
133
155
|
|
|
134
156
|
result = CallableFunction(
|
|
135
|
-
|
|
136
|
-
|
|
157
|
+
signatures=signatures,
|
|
158
|
+
py_fns=[py_fn] * len(signatures), # All signatures share the same Python function
|
|
137
159
|
self_path=function_path,
|
|
138
160
|
self_name=function_name,
|
|
139
161
|
batch_size=batch_size,
|
|
@@ -171,12 +193,12 @@ def expr_udf(*args: Any, **kwargs: Any) -> Any:
|
|
|
171
193
|
import pixeltable.exprs as exprs
|
|
172
194
|
var_exprs = [exprs.Variable(param.name, param.col_type) for param in sig.parameters.values()]
|
|
173
195
|
# call the function with the parameter expressions to construct an Expr with parameters
|
|
174
|
-
|
|
175
|
-
assert isinstance(
|
|
176
|
-
sig.return_type =
|
|
196
|
+
expr = py_fn(*var_exprs)
|
|
197
|
+
assert isinstance(expr, exprs.Expr)
|
|
198
|
+
sig.return_type = expr.col_type
|
|
177
199
|
if function_path is not None:
|
|
178
200
|
validate_symbol_path(function_path)
|
|
179
|
-
return ExprTemplateFunction(
|
|
201
|
+
return ExprTemplateFunction([ExprTemplate(expr, sig)], self_path=function_path, name=py_fn.__name__)
|
|
180
202
|
|
|
181
203
|
if len(args) == 1:
|
|
182
204
|
assert len(kwargs) == 0 and callable(args[0])
|
pixeltable/functions/globals.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import builtins
|
|
2
2
|
from typing import _GenericAlias # type: ignore[attr-defined]
|
|
3
3
|
from typing import Optional, Union
|
|
4
|
+
import typing
|
|
4
5
|
|
|
5
6
|
import sqlalchemy as sql
|
|
6
7
|
|
|
@@ -16,23 +17,24 @@ def cast(expr: exprs.Expr, target_type: Union[ts.ColumnType, type, _GenericAlias
|
|
|
16
17
|
return expr
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
T = typing.TypeVar('T')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@func.uda(allows_window=True, type_substitutions=({T: Optional[int]}, {T: Optional[float]})) # type: ignore[misc]
|
|
24
|
+
class sum(func.Aggregator, typing.Generic[T]):
|
|
23
25
|
"""Sums the selected integers or floats."""
|
|
24
26
|
def __init__(self):
|
|
25
|
-
self.sum:
|
|
27
|
+
self.sum: T = None
|
|
26
28
|
|
|
27
|
-
def update(self, val:
|
|
29
|
+
def update(self, val: T) -> None:
|
|
28
30
|
if val is None:
|
|
29
31
|
return
|
|
30
32
|
if self.sum is None:
|
|
31
33
|
self.sum = val
|
|
32
34
|
else:
|
|
33
|
-
self.sum += val
|
|
35
|
+
self.sum += val # type: ignore[operator]
|
|
34
36
|
|
|
35
|
-
def value(self) ->
|
|
37
|
+
def value(self) -> T:
|
|
36
38
|
return self.sum
|
|
37
39
|
|
|
38
40
|
|
|
@@ -43,12 +45,22 @@ def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
|
|
|
43
45
|
return sql.sql.func.sum(val)
|
|
44
46
|
|
|
45
47
|
|
|
46
|
-
@func.uda(
|
|
47
|
-
|
|
48
|
+
@func.uda(
|
|
49
|
+
allows_window=True,
|
|
50
|
+
# Allow counting non-null values of any type
|
|
51
|
+
# TODO: I couldn't include "Array" because we don't have a way to represent a generic array (of arbitrary dimension).
|
|
52
|
+
# TODO: should we have an "Any" type that can be used here?
|
|
53
|
+
type_substitutions=tuple(
|
|
54
|
+
{T: Optional[t]} # type: ignore[misc]
|
|
55
|
+
for t in (ts.String, ts.Int, ts.Float, ts.Bool, ts.Timestamp,
|
|
56
|
+
ts.Json, ts.Image, ts.Video, ts.Audio, ts.Document)
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
class count(func.Aggregator, typing.Generic[T]):
|
|
48
60
|
def __init__(self):
|
|
49
61
|
self.count = 0
|
|
50
62
|
|
|
51
|
-
def update(self, val:
|
|
63
|
+
def update(self, val: T) -> None:
|
|
52
64
|
if val is not None:
|
|
53
65
|
self.count += 1
|
|
54
66
|
|
|
@@ -62,74 +74,82 @@ def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
|
|
|
62
74
|
|
|
63
75
|
|
|
64
76
|
@func.uda(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
allows_window=True,
|
|
78
|
+
type_substitutions=tuple({T: Optional[t]} for t in (str, int, float, bool, ts.Timestamp)) # type: ignore[misc]
|
|
79
|
+
)
|
|
80
|
+
class min(func.Aggregator, typing.Generic[T]):
|
|
68
81
|
def __init__(self):
|
|
69
|
-
self.val:
|
|
82
|
+
self.val: T = None
|
|
70
83
|
|
|
71
|
-
def update(self, val:
|
|
84
|
+
def update(self, val: T) -> None:
|
|
72
85
|
if val is None:
|
|
73
86
|
return
|
|
74
87
|
if self.val is None:
|
|
75
88
|
self.val = val
|
|
76
89
|
else:
|
|
77
|
-
self.val = builtins.min(self.val, val)
|
|
90
|
+
self.val = builtins.min(self.val, val) # type: ignore[call-overload]
|
|
78
91
|
|
|
79
|
-
def value(self) ->
|
|
92
|
+
def value(self) -> T:
|
|
80
93
|
return self.val
|
|
81
94
|
|
|
82
95
|
|
|
83
96
|
@min.to_sql
|
|
84
97
|
def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
|
|
98
|
+
if val.type.python_type == bool:
|
|
99
|
+
# TODO: min/max aggregation of booleans is not supported in Postgres (but it is in Python).
|
|
100
|
+
# Right now we simply force the computation to be done in Python; we might consider implementing an alternate
|
|
101
|
+
# way of doing it in SQL. (min/max of booleans is simply logical and/or, respectively.)
|
|
102
|
+
return None
|
|
85
103
|
return sql.sql.func.min(val)
|
|
86
104
|
|
|
87
105
|
|
|
88
106
|
@func.uda(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
107
|
+
allows_window=True,
|
|
108
|
+
type_substitutions=tuple({T: Optional[t]} for t in (str, int, float, bool, ts.Timestamp)) # type: ignore[misc]
|
|
109
|
+
)
|
|
110
|
+
class max(func.Aggregator, typing.Generic[T]):
|
|
92
111
|
def __init__(self):
|
|
93
|
-
self.val:
|
|
112
|
+
self.val: T = None
|
|
94
113
|
|
|
95
|
-
def update(self, val:
|
|
114
|
+
def update(self, val: T) -> None:
|
|
96
115
|
if val is None:
|
|
97
116
|
return
|
|
98
117
|
if self.val is None:
|
|
99
118
|
self.val = val
|
|
100
119
|
else:
|
|
101
|
-
self.val = builtins.max(self.val, val)
|
|
120
|
+
self.val = builtins.max(self.val, val) # type: ignore[call-overload]
|
|
102
121
|
|
|
103
|
-
def value(self) ->
|
|
122
|
+
def value(self) -> T:
|
|
104
123
|
return self.val
|
|
105
124
|
|
|
106
125
|
|
|
107
126
|
@max.to_sql
|
|
108
127
|
def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
|
|
128
|
+
if val.type.python_type == bool:
|
|
129
|
+
# TODO: see comment in @min.to_sql.
|
|
130
|
+
return None
|
|
109
131
|
return sql.sql.func.max(val)
|
|
110
132
|
|
|
111
133
|
|
|
112
|
-
@func.uda(
|
|
113
|
-
|
|
114
|
-
requires_order_by=False)
|
|
115
|
-
class mean(func.Aggregator):
|
|
134
|
+
@func.uda(type_substitutions=({T: Optional[int]}, {T: Optional[float]})) # type: ignore[misc]
|
|
135
|
+
class mean(func.Aggregator, typing.Generic[T]):
|
|
116
136
|
def __init__(self):
|
|
117
|
-
self.sum:
|
|
137
|
+
self.sum: T = None
|
|
118
138
|
self.count = 0
|
|
119
139
|
|
|
120
|
-
def update(self, val:
|
|
140
|
+
def update(self, val: T) -> None:
|
|
121
141
|
if val is None:
|
|
122
142
|
return
|
|
123
143
|
if self.sum is None:
|
|
124
144
|
self.sum = val
|
|
125
145
|
else:
|
|
126
|
-
self.sum += val
|
|
146
|
+
self.sum += val # type: ignore[operator]
|
|
127
147
|
self.count += 1
|
|
128
148
|
|
|
129
|
-
def value(self) -> Optional[float]:
|
|
149
|
+
def value(self) -> Optional[float]: # Always a float
|
|
130
150
|
if self.count == 0:
|
|
131
151
|
return None
|
|
132
|
-
return self.sum / self.count
|
|
152
|
+
return self.sum / self.count # type: ignore[operator]
|
|
133
153
|
|
|
134
154
|
|
|
135
155
|
@mean.to_sql
|
pixeltable/functions/json.py
CHANGED
|
@@ -16,20 +16,15 @@ import pixeltable as pxt
|
|
|
16
16
|
from pixeltable.utils.code import local_public_names
|
|
17
17
|
|
|
18
18
|
|
|
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
|
-
)
|
|
19
|
+
@pxt.uda
|
|
25
20
|
class make_list(pxt.Aggregator):
|
|
26
21
|
"""
|
|
27
22
|
Collects arguments into a list.
|
|
28
23
|
"""
|
|
29
|
-
def __init__(self):
|
|
24
|
+
def __init__(self) -> None:
|
|
30
25
|
self.output: list[Any] = []
|
|
31
26
|
|
|
32
|
-
def update(self, obj:
|
|
27
|
+
def update(self, obj: pxt.Json) -> None:
|
|
33
28
|
if obj is None:
|
|
34
29
|
return
|
|
35
30
|
self.output.append(obj)
|
pixeltable/functions/ollama.py
CHANGED
|
@@ -34,7 +34,7 @@ def generate(
|
|
|
34
34
|
template: str = '',
|
|
35
35
|
context: Optional[list[int]] = None,
|
|
36
36
|
raw: bool = False,
|
|
37
|
-
format: str =
|
|
37
|
+
format: Optional[str] = None,
|
|
38
38
|
options: Optional[dict] = None,
|
|
39
39
|
) -> dict:
|
|
40
40
|
"""
|
|
@@ -44,7 +44,7 @@ def generate(
|
|
|
44
44
|
prompt: The prompt to generate a response for.
|
|
45
45
|
model: The model name.
|
|
46
46
|
suffix: The text after the model response.
|
|
47
|
-
format: The format of the response; must be one of `'json'` or `
|
|
47
|
+
format: The format of the response; must be one of `'json'` or `None`.
|
|
48
48
|
system: System message.
|
|
49
49
|
template: Prompt template to use.
|
|
50
50
|
context: The context parameter returned from a previous call to `generate()`.
|
|
@@ -77,7 +77,7 @@ def chat(
|
|
|
77
77
|
*,
|
|
78
78
|
model: str,
|
|
79
79
|
tools: Optional[list[dict]] = None,
|
|
80
|
-
format: str =
|
|
80
|
+
format: Optional[str] = None,
|
|
81
81
|
options: Optional[dict] = None,
|
|
82
82
|
) -> dict:
|
|
83
83
|
"""
|
|
@@ -87,7 +87,7 @@ def chat(
|
|
|
87
87
|
messages: The messages of the chat.
|
|
88
88
|
model: The model name.
|
|
89
89
|
tools: Tools for the model to use.
|
|
90
|
-
format: The format of the response; must be one of `'json'` or `
|
|
90
|
+
format: The format of the response; must be one of `'json'` or `None`.
|
|
91
91
|
options: Additional options to pass to the `chat` call, such as `max_tokens`, `temperature`, `top_p`, and `top_k`.
|
|
92
92
|
For details, see the
|
|
93
93
|
[Valid Parameters and Values](https://github.com/ollama/ollama/blob/main/docs/modelfile.md#valid-parameters-and-values)
|
|
@@ -232,7 +232,7 @@ def _(
|
|
|
232
232
|
sql.cast(day, sql.Integer),
|
|
233
233
|
sql.cast(hour, sql.Integer),
|
|
234
234
|
sql.cast(minute, sql.Integer),
|
|
235
|
-
sql.cast(second + microsecond / 1000000.0, sql.
|
|
235
|
+
sql.cast(second + microsecond / 1000000.0, sql.Float))
|
|
236
236
|
|
|
237
237
|
# @pxt.udf
|
|
238
238
|
# def date(self: datetime) -> datetime:
|
pixeltable/functions/video.py
CHANGED
|
@@ -47,13 +47,7 @@ _format_defaults = { # format -> (codec, ext)
|
|
|
47
47
|
# output_container.mux(packet)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
@pxt.uda(
|
|
51
|
-
init_types=[pxt.IntType()],
|
|
52
|
-
update_types=[pxt.ImageType()],
|
|
53
|
-
value_type=pxt.VideoType(),
|
|
54
|
-
requires_order_by=True,
|
|
55
|
-
allows_window=False,
|
|
56
|
-
)
|
|
50
|
+
@pxt.uda(requires_order_by=True)
|
|
57
51
|
class make_video(pxt.Aggregator):
|
|
58
52
|
"""
|
|
59
53
|
Aggregator that creates a video from a sequence of images.
|
|
@@ -80,7 +74,7 @@ class make_video(pxt.Aggregator):
|
|
|
80
74
|
for packet in self.stream.encode(av_frame):
|
|
81
75
|
self.container.mux(packet)
|
|
82
76
|
|
|
83
|
-
def value(self) ->
|
|
77
|
+
def value(self) -> pxt.Video:
|
|
84
78
|
for packet in self.stream.encode():
|
|
85
79
|
self.container.mux(packet)
|
|
86
80
|
self.container.close()
|
pixeltable/functions/vision.py
CHANGED
|
@@ -220,7 +220,7 @@ def eval_detections(
|
|
|
220
220
|
return result
|
|
221
221
|
|
|
222
222
|
|
|
223
|
-
@pxt.uda
|
|
223
|
+
@pxt.uda
|
|
224
224
|
class mean_ap(pxt.Aggregator):
|
|
225
225
|
"""
|
|
226
226
|
Calculates the mean average precision (mAP) over
|