pixeltable 0.2.27__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.

Files changed (50) hide show
  1. pixeltable/__version__.py +2 -2
  2. pixeltable/catalog/__init__.py +1 -1
  3. pixeltable/catalog/dir.py +6 -0
  4. pixeltable/catalog/globals.py +13 -0
  5. pixeltable/catalog/named_function.py +4 -0
  6. pixeltable/catalog/path_dict.py +37 -11
  7. pixeltable/catalog/schema_object.py +6 -0
  8. pixeltable/catalog/table.py +22 -5
  9. pixeltable/catalog/table_version.py +22 -8
  10. pixeltable/dataframe.py +201 -3
  11. pixeltable/env.py +9 -3
  12. pixeltable/exec/expr_eval_node.py +1 -1
  13. pixeltable/exec/sql_node.py +2 -2
  14. pixeltable/exprs/expr.py +1 -0
  15. pixeltable/exprs/function_call.py +134 -24
  16. pixeltable/exprs/inline_expr.py +22 -2
  17. pixeltable/exprs/row_builder.py +1 -1
  18. pixeltable/exprs/similarity_expr.py +9 -2
  19. pixeltable/func/aggregate_function.py +148 -68
  20. pixeltable/func/callable_function.py +49 -13
  21. pixeltable/func/expr_template_function.py +55 -24
  22. pixeltable/func/function.py +183 -22
  23. pixeltable/func/function_registry.py +2 -1
  24. pixeltable/func/query_template_function.py +11 -6
  25. pixeltable/func/signature.py +64 -7
  26. pixeltable/func/udf.py +57 -35
  27. pixeltable/functions/globals.py +54 -34
  28. pixeltable/functions/json.py +3 -8
  29. pixeltable/functions/ollama.py +4 -4
  30. pixeltable/functions/timestamp.py +1 -1
  31. pixeltable/functions/video.py +3 -9
  32. pixeltable/functions/vision.py +1 -1
  33. pixeltable/globals.py +218 -59
  34. pixeltable/index/embedding_index.py +44 -24
  35. pixeltable/metadata/__init__.py +1 -1
  36. pixeltable/metadata/converters/convert_16.py +2 -1
  37. pixeltable/metadata/converters/convert_17.py +2 -1
  38. pixeltable/metadata/converters/convert_23.py +35 -0
  39. pixeltable/metadata/converters/convert_24.py +47 -0
  40. pixeltable/metadata/converters/util.py +4 -2
  41. pixeltable/metadata/notes.py +2 -0
  42. pixeltable/metadata/schema.py +1 -0
  43. pixeltable/tool/create_test_db_dump.py +11 -0
  44. pixeltable/tool/doc_plugins/griffe.py +4 -3
  45. pixeltable/type_system.py +182 -47
  46. {pixeltable-0.2.27.dist-info → pixeltable-0.2.29.dist-info}/METADATA +3 -2
  47. {pixeltable-0.2.27.dist-info → pixeltable-0.2.29.dist-info}/RECORD +50 -48
  48. {pixeltable-0.2.27.dist-info → pixeltable-0.2.29.dist-info}/LICENSE +0 -0
  49. {pixeltable-0.2.27.dist-info → pixeltable-0.2.29.dist-info}/WHEEL +0 -0
  50. {pixeltable-0.2.27.dist-info → pixeltable-0.2.29.dist-info}/entry_points.txt +0 -0
@@ -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
- cls, py_fn: Optional[Callable] = None, py_params: Optional[list[inspect.Parameter]] = None,
160
- param_types: Optional[list[ts.ColumnType]] = None
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
- param_type, is_batched = cls._infer_type(param.annotation)
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, py_fn: Callable,
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
- parameters = cls.create_parameters(py_fn=py_fn, param_types=param_types)
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
- return_type, return_is_batched = cls._infer_type(sig.return_annotation)
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
- batch_size: Optional[int] = None,
26
- substitute_fn: Optional[Callable] = None,
27
- is_method: bool = False,
28
- is_property: bool = False,
29
- _force_stored: bool = False
30
- ) -> Callable[[Callable], Function]: ...
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
- ) -> Function:
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
- sig = Signature.create(decorated_fn, param_types, return_type)
108
-
109
- # batched functions must have a batched return type
110
- # TODO: remove 'Python' from the error messages when we have full inference with Annotated types
111
- if batch_size is not None and not sig.is_batched:
112
- raise excs.Error(f'{errmsg_name}(): batch_size is specified; Python return type must be a `Batch`')
113
- if batch_size is not None and len(sig.batched_parameters) == 0:
114
- raise excs.Error(f'{errmsg_name}(): batch_size is specified; at least one Python parameter must be `Batch`')
115
- if batch_size is None and len(sig.batched_parameters) > 0:
116
- raise excs.Error(f'{errmsg_name}(): batched parameters in udf, but no `batch_size` given')
117
-
118
- if is_method and is_property:
119
- raise excs.Error(f'Cannot specify both `is_method` and `is_property` (in function `{function_name}`)')
120
- if is_property and len(sig.parameters) != 1:
121
- raise excs.Error(
122
- f"`is_property=True` expects a UDF with exactly 1 parameter, but `{function_name}` has {len(sig.parameters)}"
123
- )
124
- if (is_method or is_property) and function_path is None:
125
- raise excs.Error('Stored functions cannot be declared using `is_method` or `is_property`')
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
- signature=sig,
136
- py_fn=py_fn,
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
- template = py_fn(*var_exprs)
175
- assert isinstance(template, exprs.Expr)
176
- sig.return_type = template.col_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(template, sig, self_path=function_path, name=py_fn.__name__)
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])
@@ -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
- @func.uda(
20
- update_types=[ts.IntType(nullable=True)], value_type=ts.IntType(nullable=False),
21
- allows_window=True, requires_order_by=False)
22
- class sum(func.Aggregator):
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: Optional[int] = None
27
+ self.sum: T = None
26
28
 
27
- def update(self, val: Optional[int]) -> None:
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) -> Union[int, float]:
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(update_types=[ts.IntType(nullable=True)], value_type=ts.IntType(), allows_window=True, requires_order_by=False)
47
- class count(func.Aggregator):
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: Optional[int]) -> None:
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
- update_types=[ts.IntType(nullable=True)], value_type=ts.IntType(nullable=True), allows_window=True,
66
- requires_order_by=False)
67
- class min(func.Aggregator):
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: Optional[int] = None
82
+ self.val: T = None
70
83
 
71
- def update(self, val: Optional[int]) -> None:
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) -> Optional[int]:
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
- update_types=[ts.IntType(nullable=True)], value_type=ts.IntType(nullable=True), allows_window=True,
90
- requires_order_by=False)
91
- class max(func.Aggregator):
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: Optional[int] = None
112
+ self.val: T = None
94
113
 
95
- def update(self, val: Optional[int]) -> None:
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) -> Optional[int]:
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
- update_types=[ts.IntType(nullable=True)], value_type=ts.FloatType(nullable=True), allows_window=False,
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: Optional[int] = None
137
+ self.sum: T = None
118
138
  self.count = 0
119
139
 
120
- def update(self, val: Optional[int]) -> None:
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
@@ -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: Any) -> None:
27
+ def update(self, obj: pxt.Json) -> None:
33
28
  if obj is None:
34
29
  return
35
30
  self.output.append(obj)
@@ -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 `''` (the empty string).
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 `''` (the empty string).
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.Double))
235
+ sql.cast(second + microsecond / 1000000.0, sql.Float))
236
236
 
237
237
  # @pxt.udf
238
238
  # def date(self: datetime) -> datetime:
@@ -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) -> str:
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()
@@ -132,7 +126,7 @@ def _get_metadata(path: str) -> dict:
132
126
  assert isinstance(container, av.container.InputContainer)
133
127
  streams_info = [__get_stream_metadata(stream) for stream in container.streams]
134
128
  result = {
135
- 'bit_exact': container.bit_exact,
129
+ 'bit_exact': getattr(container, 'bit_exact', False),
136
130
  'bit_rate': container.bit_rate,
137
131
  'size': container.size,
138
132
  'metadata': container.metadata,
@@ -220,7 +220,7 @@ def eval_detections(
220
220
  return result
221
221
 
222
222
 
223
- @pxt.uda(update_types=[pxt.JsonType()], value_type=pxt.JsonType(), allows_std_agg=True, allows_window=False)
223
+ @pxt.uda
224
224
  class mean_ap(pxt.Aggregator):
225
225
  """
226
226
  Calculates the mean average precision (mAP) over