pixeltable 0.3.13__py3-none-any.whl → 0.3.15__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 +2 -2
- pixeltable/__version__.py +2 -2
- pixeltable/catalog/catalog.py +9 -7
- pixeltable/catalog/column.py +6 -2
- pixeltable/catalog/dir.py +2 -1
- pixeltable/catalog/insertable_table.py +1 -1
- pixeltable/catalog/schema_object.py +2 -1
- pixeltable/catalog/table.py +12 -8
- pixeltable/catalog/table_version.py +21 -0
- pixeltable/catalog/view.py +3 -3
- pixeltable/dataframe.py +48 -5
- pixeltable/env.py +1 -1
- pixeltable/exec/aggregation_node.py +14 -0
- pixeltable/exec/cache_prefetch_node.py +1 -1
- pixeltable/exec/expr_eval/expr_eval_node.py +1 -1
- pixeltable/exprs/column_ref.py +42 -17
- pixeltable/exprs/data_row.py +3 -0
- pixeltable/exprs/globals.py +1 -1
- pixeltable/exprs/literal.py +11 -1
- pixeltable/exprs/rowid_ref.py +4 -1
- pixeltable/exprs/similarity_expr.py +1 -1
- pixeltable/func/function.py +1 -1
- pixeltable/functions/__init__.py +1 -0
- pixeltable/functions/date.py +185 -0
- pixeltable/functions/gemini.py +184 -49
- pixeltable/functions/globals.py +1 -16
- pixeltable/functions/json.py +2 -1
- pixeltable/functions/math.py +103 -0
- pixeltable/functions/string.py +1 -2
- pixeltable/functions/video.py +2 -2
- pixeltable/globals.py +26 -9
- pixeltable/io/hf_datasets.py +2 -2
- pixeltable/io/pandas.py +16 -4
- pixeltable/io/parquet.py +4 -2
- pixeltable/metadata/__init__.py +1 -1
- pixeltable/metadata/converters/convert_34.py +21 -0
- pixeltable/metadata/notes.py +1 -0
- pixeltable/plan.py +12 -5
- pixeltable/share/__init__.py +1 -1
- pixeltable/share/packager.py +397 -120
- pixeltable/share/publish.py +61 -16
- pixeltable/store.py +57 -20
- pixeltable/type_system.py +46 -2
- pixeltable/utils/arrow.py +8 -2
- pixeltable/utils/pytorch.py +4 -0
- {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/METADATA +2 -4
- {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/RECORD +50 -48
- {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/LICENSE +0 -0
- {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/WHEEL +0 -0
- {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/entry_points.txt +0 -0
pixeltable/exprs/literal.py
CHANGED
|
@@ -50,6 +50,9 @@ class Literal(Expr):
|
|
|
50
50
|
assert isinstance(self.val, datetime.datetime)
|
|
51
51
|
default_tz = Env.get().default_time_zone
|
|
52
52
|
return f"'{self.val.astimezone(default_tz).isoformat()}'"
|
|
53
|
+
if self.col_type.is_date_type():
|
|
54
|
+
assert isinstance(self.val, datetime.date)
|
|
55
|
+
return f"'{self.val.isoformat()}'"
|
|
53
56
|
if self.col_type.is_array_type():
|
|
54
57
|
assert isinstance(self.val, np.ndarray)
|
|
55
58
|
return str(self.val.tolist())
|
|
@@ -82,6 +85,10 @@ class Literal(Expr):
|
|
|
82
85
|
# stored as UTC in the database)
|
|
83
86
|
encoded_val = self.val.isoformat()
|
|
84
87
|
return {'val': encoded_val, 'val_t': self.col_type._type.name, **super()._as_dict()}
|
|
88
|
+
elif self.col_type.is_date_type():
|
|
89
|
+
assert isinstance(self.val, datetime.date)
|
|
90
|
+
encoded_val = self.val.isoformat()
|
|
91
|
+
return {'val': encoded_val, 'val_t': self.col_type._type.name, **super()._as_dict()}
|
|
85
92
|
elif self.col_type.is_array_type():
|
|
86
93
|
assert isinstance(self.val, np.ndarray)
|
|
87
94
|
return {'val': self.val.tolist(), 'val_t': self.col_type._type.name, **super()._as_dict()}
|
|
@@ -96,7 +103,10 @@ class Literal(Expr):
|
|
|
96
103
|
assert 'val' in d
|
|
97
104
|
if 'val_t' in d:
|
|
98
105
|
val_t = d['val_t']
|
|
99
|
-
if val_t == ts.ColumnType.Type.
|
|
106
|
+
if val_t == ts.ColumnType.Type.DATE.name:
|
|
107
|
+
dt = datetime.date.fromisoformat(d['val'])
|
|
108
|
+
return cls(dt)
|
|
109
|
+
elif val_t == ts.ColumnType.Type.TIMESTAMP.name:
|
|
100
110
|
dt = datetime.datetime.fromisoformat(d['val'])
|
|
101
111
|
assert dt.tzinfo == datetime.timezone.utc # Must be UTC in the database
|
|
102
112
|
return cls(dt)
|
pixeltable/exprs/rowid_ref.py
CHANGED
|
@@ -30,7 +30,7 @@ class RowidRef(Expr):
|
|
|
30
30
|
|
|
31
31
|
def __init__(
|
|
32
32
|
self,
|
|
33
|
-
tbl: catalog.TableVersionHandle,
|
|
33
|
+
tbl: Optional[catalog.TableVersionHandle],
|
|
34
34
|
idx: int,
|
|
35
35
|
tbl_id: Optional[UUID] = None,
|
|
36
36
|
normalized_base_id: Optional[UUID] = None,
|
|
@@ -98,6 +98,9 @@ class RowidRef(Expr):
|
|
|
98
98
|
def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
|
|
99
99
|
tbl = self.tbl.get() if self.tbl is not None else catalog.Catalog.get().get_tbl_version(self.tbl_id, None)
|
|
100
100
|
rowid_cols = tbl.store_tbl.rowid_columns()
|
|
101
|
+
assert self.rowid_component_idx <= len(rowid_cols), (
|
|
102
|
+
f'{self.rowid_component_idx} not consistent with {rowid_cols}'
|
|
103
|
+
)
|
|
101
104
|
return rowid_cols[self.rowid_component_idx]
|
|
102
105
|
|
|
103
106
|
def eval(self, data_row: DataRow, row_builder: RowBuilder) -> None:
|
|
@@ -26,7 +26,7 @@ class SimilarityExpr(Expr):
|
|
|
26
26
|
from pixeltable import index
|
|
27
27
|
|
|
28
28
|
# determine index to use
|
|
29
|
-
idx_dict =
|
|
29
|
+
idx_dict = col_ref.find_embedding_index(idx_name, 'similarity')
|
|
30
30
|
assert len(idx_dict) == 1
|
|
31
31
|
self.idx_info = next(iter(idx_dict.values()))
|
|
32
32
|
idx = self.idx_info.idx
|
pixeltable/func/function.py
CHANGED
|
@@ -514,7 +514,7 @@ class InvalidFunction(Function):
|
|
|
514
514
|
def _as_dict(self) -> dict:
|
|
515
515
|
"""
|
|
516
516
|
Here we write out (verbatim) the original metadata that failed to load (and that resulted in the
|
|
517
|
-
InvalidFunction). Note that the InvalidFunction itself is never
|
|
517
|
+
InvalidFunction). Note that the InvalidFunction itself is never serialized, so there is no corresponding
|
|
518
518
|
from_dict() method.
|
|
519
519
|
"""
|
|
520
520
|
return self.fn_dict
|
pixeltable/functions/__init__.py
CHANGED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pixeltable [UDFs](https://pixeltable.readme.io/docs/user-defined-functions-udfs) for `DateType`.
|
|
3
|
+
|
|
4
|
+
Usage example:
|
|
5
|
+
```python
|
|
6
|
+
import pixeltable as pxt
|
|
7
|
+
|
|
8
|
+
t = pxt.get_table(...)
|
|
9
|
+
t.select(t.date_col.year, t.date_col.weekday()).collect()
|
|
10
|
+
```
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from datetime import date, timedelta
|
|
14
|
+
|
|
15
|
+
import sqlalchemy as sql
|
|
16
|
+
|
|
17
|
+
import pixeltable as pxt
|
|
18
|
+
from pixeltable.utils.code import local_public_names
|
|
19
|
+
|
|
20
|
+
_SQL_ZERO = sql.literal(0)
|
|
21
|
+
|
|
22
|
+
# NOT YET SUPPORTED date +/- integer
|
|
23
|
+
# NOT YET SUPPORTED date1 - date2 -> integer
|
|
24
|
+
# NOT YET SUPPORTED timestamp(date)
|
|
25
|
+
# NOT YET SUPPORTED date(timestamp)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pxt.udf(is_property=True)
|
|
29
|
+
def year(self: date) -> int:
|
|
30
|
+
"""
|
|
31
|
+
Between [`MINYEAR`](https://docs.python.org/3/library/datetime.html#datetime.MINYEAR) and
|
|
32
|
+
[`MAXYEAR`](https://docs.python.org/3/library/datetime.html#datetime.MAXYEAR) inclusive.
|
|
33
|
+
|
|
34
|
+
Equivalent to [`date.year`](https://docs.python.org/3/library/datetime.html#datetime.date.year).
|
|
35
|
+
"""
|
|
36
|
+
return self.year
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@year.to_sql
|
|
40
|
+
def _(self: sql.ColumnElement) -> sql.ColumnElement:
|
|
41
|
+
return sql.extract('year', self)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pxt.udf(is_property=True)
|
|
45
|
+
def month(self: date) -> int:
|
|
46
|
+
"""
|
|
47
|
+
Between 1 and 12 inclusive.
|
|
48
|
+
|
|
49
|
+
Equivalent to [`date.month`](https://docs.python.org/3/library/datetime.html#datetime.date.month).
|
|
50
|
+
"""
|
|
51
|
+
return self.month
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@month.to_sql
|
|
55
|
+
def _(self: sql.ColumnElement) -> sql.ColumnElement:
|
|
56
|
+
return sql.extract('month', self)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pxt.udf(is_property=True)
|
|
60
|
+
def day(self: date) -> int:
|
|
61
|
+
"""
|
|
62
|
+
Between 1 and the number of days in the given month of the given year.
|
|
63
|
+
|
|
64
|
+
Equivalent to [`date.day`](https://docs.python.org/3/library/datetime.html#datetime.date.day).
|
|
65
|
+
"""
|
|
66
|
+
return self.day
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@day.to_sql
|
|
70
|
+
def _(self: sql.ColumnElement) -> sql.ColumnElement:
|
|
71
|
+
return sql.extract('day', self)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@pxt.udf(is_method=True)
|
|
75
|
+
def make_date(year: int, month: int, day: int) -> date:
|
|
76
|
+
"""
|
|
77
|
+
Create a date.
|
|
78
|
+
|
|
79
|
+
Equivalent to [`datetime()`](https://docs.python.org/3/library/datetime.html#datetime.date).
|
|
80
|
+
"""
|
|
81
|
+
return date(year, month, day)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@make_date.to_sql
|
|
85
|
+
def _(year: sql.ColumnElement, month: sql.ColumnElement, day: sql.ColumnElement) -> sql.ColumnElement:
|
|
86
|
+
return sql.func.make_date(sql.cast(year, sql.Integer), sql.cast(month, sql.Integer), sql.cast(day, sql.Integer))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@pxt.udf(is_method=True)
|
|
90
|
+
def weekday(self: date) -> int:
|
|
91
|
+
"""
|
|
92
|
+
Between 0 (Monday) and 6 (Sunday) inclusive.
|
|
93
|
+
|
|
94
|
+
Equivalent to [`date.weekday()`](https://docs.python.org/3/library/datetime.html#datetime.date.weekday).
|
|
95
|
+
"""
|
|
96
|
+
return self.weekday()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@weekday.to_sql
|
|
100
|
+
def _(self: sql.ColumnElement) -> sql.ColumnElement:
|
|
101
|
+
return sql.extract('isodow', self) - 1
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@pxt.udf(is_method=True)
|
|
105
|
+
def isoweekday(self: date) -> int:
|
|
106
|
+
"""
|
|
107
|
+
Return the day of the week as an integer, where Monday is 1 and Sunday is 7.
|
|
108
|
+
|
|
109
|
+
Equivalent to [`date.isoweekday()`](https://docs.python.org/3/library/datetime.html#datetime.date.isoweekday).
|
|
110
|
+
"""
|
|
111
|
+
return self.isoweekday()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@isoweekday.to_sql
|
|
115
|
+
def _(self: sql.ColumnElement) -> sql.ColumnElement:
|
|
116
|
+
return sql.extract('isodow', self)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@pxt.udf(is_method=True)
|
|
120
|
+
def isocalendar(self: date) -> dict:
|
|
121
|
+
"""
|
|
122
|
+
Return a dictionary with three entries: `'year'`, `'week'`, and `'weekday'`.
|
|
123
|
+
|
|
124
|
+
Equivalent to
|
|
125
|
+
[`date.isocalendar()`](https://docs.python.org/3/library/datetime.html#datetime.date.isocalendar).
|
|
126
|
+
"""
|
|
127
|
+
iso_year, iso_week, iso_weekday = self.isocalendar()
|
|
128
|
+
return {'year': iso_year, 'week': iso_week, 'weekday': iso_weekday}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@pxt.udf(is_method=True)
|
|
132
|
+
def isoformat(self: date, sep: str = 'T', timespec: str = 'auto') -> str:
|
|
133
|
+
"""
|
|
134
|
+
Return a string representing the date and time in ISO 8601 format.
|
|
135
|
+
|
|
136
|
+
Equivalent to [`date.isoformat()`](https://docs.python.org/3/library/datetime.html#datetime.date.isoformat).
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
sep: Separator between date and time.
|
|
140
|
+
timespec: The number of additional terms in the output. See the
|
|
141
|
+
[`date.isoformat()`](https://docs.python.org/3/library/datetime.html#datetime.date.isoformat)
|
|
142
|
+
documentation for more details.
|
|
143
|
+
"""
|
|
144
|
+
return self.isoformat()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@pxt.udf(is_method=True)
|
|
148
|
+
def toordinal(self: date) -> int:
|
|
149
|
+
"""
|
|
150
|
+
Return the proleptic Gregorian ordinal of the date, where January 1 of year 1 has ordinal 1.
|
|
151
|
+
|
|
152
|
+
Equivalent to [`date.toordinal()`](https://docs.python.org/3/library/datetime.html#datetime.date.toordinal).
|
|
153
|
+
"""
|
|
154
|
+
return self.toordinal()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@pxt.udf(is_method=True)
|
|
158
|
+
def strftime(self: date, format: str) -> str:
|
|
159
|
+
"""
|
|
160
|
+
Return a string representing the date and time, controlled by an explicit format string.
|
|
161
|
+
|
|
162
|
+
Equivalent to [`date.strftime()`](https://docs.python.org/3/library/datetime.html#datetime.date.strftime).
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
format: The format string to control the output. For a complete list of formatting directives, see
|
|
166
|
+
[`strftime()` and `strptime()` Behavior](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior).
|
|
167
|
+
"""
|
|
168
|
+
return self.strftime(format)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@pxt.udf(is_method=True)
|
|
172
|
+
def add_days(self: date, n: int) -> date:
|
|
173
|
+
"""
|
|
174
|
+
Add `n` days to the date.
|
|
175
|
+
|
|
176
|
+
Equivalent to [`date + timedelta(days=n)`](https://docs.python.org/3/library/datetime.html#datetime.timedelta).
|
|
177
|
+
"""
|
|
178
|
+
return self + timedelta(days=n)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
__all__ = local_public_names(__name__)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def __dir__() -> list[str]:
|
|
185
|
+
return __all__
|
pixeltable/functions/gemini.py
CHANGED
|
@@ -1,46 +1,43 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Pixeltable [UDFs](https://pixeltable.readme.io/docs/user-defined-functions-udfs)
|
|
3
3
|
that wrap various endpoints from the Google Gemini API. In order to use them, you must
|
|
4
|
-
first `pip install google-
|
|
4
|
+
first `pip install google-genai` and configure your Gemini credentials, as described in
|
|
5
5
|
the [Working with Gemini](https://pixeltable.readme.io/docs/working-with-gemini) tutorial.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import asyncio
|
|
9
|
+
import io
|
|
10
|
+
import tempfile
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING, Optional
|
|
13
|
+
|
|
14
|
+
import PIL.Image
|
|
9
15
|
|
|
10
16
|
import pixeltable as pxt
|
|
11
|
-
from pixeltable import env
|
|
17
|
+
from pixeltable import env, exceptions as excs, exprs
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from google import genai
|
|
12
21
|
|
|
13
22
|
|
|
14
23
|
@env.register_client('gemini')
|
|
15
|
-
def _(api_key: str) ->
|
|
16
|
-
|
|
24
|
+
def _(api_key: str) -> 'genai.client.Client':
|
|
25
|
+
from google import genai
|
|
17
26
|
|
|
18
|
-
genai.
|
|
27
|
+
return genai.client.Client(api_key=api_key)
|
|
19
28
|
|
|
20
29
|
|
|
21
|
-
def
|
|
22
|
-
env.Env.get().get_client('gemini')
|
|
30
|
+
def _genai_client() -> 'genai.client.Client':
|
|
31
|
+
return env.Env.get().get_client('gemini')
|
|
23
32
|
|
|
24
33
|
|
|
25
34
|
@pxt.udf(resource_pool='request-rate:gemini')
|
|
26
35
|
async def generate_content(
|
|
27
|
-
contents: str,
|
|
28
|
-
*,
|
|
29
|
-
model_name: str,
|
|
30
|
-
candidate_count: Optional[int] = None,
|
|
31
|
-
stop_sequences: Optional[list[str]] = None,
|
|
32
|
-
max_output_tokens: Optional[int] = None,
|
|
33
|
-
temperature: Optional[float] = None,
|
|
34
|
-
top_p: Optional[float] = None,
|
|
35
|
-
top_k: Optional[int] = None,
|
|
36
|
-
response_mime_type: Optional[str] = None,
|
|
37
|
-
response_schema: Optional[dict] = None,
|
|
38
|
-
presence_penalty: Optional[float] = None,
|
|
39
|
-
frequency_penalty: Optional[float] = None,
|
|
36
|
+
contents: str, *, model: str, config: Optional[dict] = None, tools: Optional[list[dict]] = None
|
|
40
37
|
) -> dict:
|
|
41
38
|
"""
|
|
42
39
|
Generate content from the specified model. For additional details, see:
|
|
43
|
-
<https://ai.google.dev/gemini-api/docs>
|
|
40
|
+
<https://ai.google.dev/gemini-api/docs/text-generation>
|
|
44
41
|
|
|
45
42
|
Request throttling:
|
|
46
43
|
Applies the rate limit set in the config (section `gemini`, key `rate_limit`). If no rate
|
|
@@ -48,44 +45,182 @@ async def generate_content(
|
|
|
48
45
|
|
|
49
46
|
__Requirements:__
|
|
50
47
|
|
|
51
|
-
- `pip install google-
|
|
48
|
+
- `pip install google-genai`
|
|
52
49
|
|
|
53
50
|
Args:
|
|
54
51
|
contents: The input content to generate from.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
model: The name of the model to use.
|
|
53
|
+
config: Configuration for generation, corresponding to keyword arguments of
|
|
54
|
+
`genai.types.GenerateContentConfig`. For details on the parameters, see:
|
|
55
|
+
<https://googleapis.github.io/python-genai/genai.html#module-genai.types>
|
|
56
|
+
tools: Optional list of Pixeltable tools to use. It is also possible to specify tools manually via the
|
|
57
|
+
`config.tools` parameter, but at most one of `config.tools` or `tools` may be used.
|
|
58
58
|
|
|
59
59
|
Returns:
|
|
60
60
|
A dictionary containing the response and other metadata.
|
|
61
61
|
|
|
62
62
|
Examples:
|
|
63
|
-
Add a computed column that applies the model `gemini-
|
|
63
|
+
Add a computed column that applies the model `gemini-2.0-flash`
|
|
64
64
|
to an existing Pixeltable column `tbl.prompt` of the table `tbl`:
|
|
65
65
|
|
|
66
|
-
>>> tbl.add_computed_column(response=generate_content(tbl.prompt,
|
|
66
|
+
>>> tbl.add_computed_column(response=generate_content(tbl.prompt, model='gemini-2.0-flash'))
|
|
67
67
|
"""
|
|
68
|
-
env.Env.get().require_package('google.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
|
|
68
|
+
env.Env.get().require_package('google.genai')
|
|
69
|
+
from google.genai import types
|
|
70
|
+
|
|
71
|
+
config_: types.GenerateContentConfig
|
|
72
|
+
if config is None and tools is None:
|
|
73
|
+
config_ = None
|
|
74
|
+
else:
|
|
75
|
+
if config is None:
|
|
76
|
+
config_ = types.GenerateContentConfig()
|
|
77
|
+
else:
|
|
78
|
+
config_ = types.GenerateContentConfig(**config)
|
|
79
|
+
if tools is not None:
|
|
80
|
+
gemini_tools = [__convert_pxt_tool(tool) for tool in tools]
|
|
81
|
+
config_.tools = [types.Tool(function_declarations=gemini_tools)]
|
|
82
|
+
|
|
83
|
+
response = await _genai_client().aio.models.generate_content(model=model, contents=contents, config=config_)
|
|
84
|
+
return response.model_dump()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def __convert_pxt_tool(pxt_tool: dict) -> dict:
|
|
88
|
+
return {
|
|
89
|
+
'name': pxt_tool['name'],
|
|
90
|
+
'description': pxt_tool['description'],
|
|
91
|
+
'parameters': {
|
|
92
|
+
'type': 'object',
|
|
93
|
+
'properties': pxt_tool['parameters']['properties'],
|
|
94
|
+
'required': pxt_tool['required'],
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def invoke_tools(tools: pxt.func.Tools, response: exprs.Expr) -> exprs.InlineDict:
|
|
100
|
+
"""Converts an OpenAI response dict to Pixeltable tool invocation format and calls `tools._invoke()`."""
|
|
101
|
+
return tools._invoke(_gemini_response_to_pxt_tool_calls(response))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@pxt.udf
|
|
105
|
+
def _gemini_response_to_pxt_tool_calls(response: dict) -> Optional[dict]:
|
|
106
|
+
print(response)
|
|
107
|
+
pxt_tool_calls: dict[str, list[dict]] = {}
|
|
108
|
+
for part in response['candidates'][0]['content']['parts']:
|
|
109
|
+
tool_call = part.get('function_call')
|
|
110
|
+
if tool_call is not None:
|
|
111
|
+
tool_name = tool_call['name']
|
|
112
|
+
if tool_name not in pxt_tool_calls:
|
|
113
|
+
pxt_tool_calls[tool_name] = []
|
|
114
|
+
pxt_tool_calls[tool_name].append({'args': tool_call['args']})
|
|
115
|
+
if len(pxt_tool_calls) == 0:
|
|
116
|
+
return None
|
|
117
|
+
return pxt_tool_calls
|
|
87
118
|
|
|
88
119
|
|
|
89
120
|
@generate_content.resource_pool
|
|
90
|
-
def _(
|
|
91
|
-
return f'request-rate:gemini:{
|
|
121
|
+
def _(model: str) -> str:
|
|
122
|
+
return f'request-rate:gemini:{model}'
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@pxt.udf(resource_pool='request-rate:imagen')
|
|
126
|
+
async def generate_images(prompt: str, *, model: str, config: Optional[dict] = None) -> PIL.Image.Image:
|
|
127
|
+
"""
|
|
128
|
+
Generates images based on a text description and configuration. For additional details, see:
|
|
129
|
+
<https://ai.google.dev/gemini-api/docs/image-generation>
|
|
130
|
+
|
|
131
|
+
__Requirements:__
|
|
132
|
+
|
|
133
|
+
- `pip install google-genai`
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
prompt: A text description of the images to generate.
|
|
137
|
+
model: The model to use.
|
|
138
|
+
config: Configuration for generation, corresponding to keyword arguments of
|
|
139
|
+
`genai.types.GenerateImagesConfig`. For details on the parameters, see:
|
|
140
|
+
<https://googleapis.github.io/python-genai/genai.html#module-genai.types>
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
The generated image.
|
|
144
|
+
|
|
145
|
+
Examples:
|
|
146
|
+
Add a computed column that applies the model `imagen-3.0-generate-002`
|
|
147
|
+
to an existing Pixeltable column `tbl.prompt` of the table `tbl`:
|
|
148
|
+
|
|
149
|
+
>>> tbl.add_computed_column(response=generate_images(tbl.prompt, model='imagen-3.0-generate-002'))
|
|
150
|
+
"""
|
|
151
|
+
env.Env.get().require_package('google.genai')
|
|
152
|
+
from google.genai.types import GenerateImagesConfig
|
|
153
|
+
|
|
154
|
+
config_ = GenerateImagesConfig(**config) if config else None
|
|
155
|
+
response = await _genai_client().aio.models.generate_images(model=model, prompt=prompt, config=config_)
|
|
156
|
+
return response.generated_images[0].image._pil_image
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@generate_images.resource_pool
|
|
160
|
+
def _(model: str) -> str:
|
|
161
|
+
return f'request-rate:imagen:{model}'
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@pxt.udf(resource_pool='request-rate:veo')
|
|
165
|
+
async def generate_videos(
|
|
166
|
+
prompt: Optional[str] = None, image: Optional[PIL.Image.Image] = None, *, model: str, config: Optional[dict] = None
|
|
167
|
+
) -> pxt.Video:
|
|
168
|
+
"""
|
|
169
|
+
Generates videos based on a text description and configuration. For additional details, see:
|
|
170
|
+
<https://ai.google.dev/gemini-api/docs/video-generation>
|
|
171
|
+
|
|
172
|
+
__Requirements:__
|
|
173
|
+
|
|
174
|
+
- `pip install google-genai`
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
prompt: A text description of the videos to generate.
|
|
178
|
+
image: An optional image to use as the first frame of the video. At least one of `prompt` or `image` must be
|
|
179
|
+
provided. (It is ok to specify both.)
|
|
180
|
+
model: The model to use.
|
|
181
|
+
config: Configuration for generation, corresponding to keyword arguments of
|
|
182
|
+
`genai.types.GenerateVideosConfig`. For details on the parameters, see:
|
|
183
|
+
<https://googleapis.github.io/python-genai/genai.html#module-genai.types>
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
The generated video.
|
|
187
|
+
|
|
188
|
+
Examples:
|
|
189
|
+
Add a computed column that applies the model `veo-2.0-generate-001`
|
|
190
|
+
to an existing Pixeltable column `tbl.prompt` of the table `tbl`:
|
|
191
|
+
|
|
192
|
+
>>> tbl.add_computed_column(response=generate_videos(tbl.prompt, model='veo-2.0-generate-001'))
|
|
193
|
+
"""
|
|
194
|
+
env.Env.get().require_package('google.genai')
|
|
195
|
+
from google.genai import types
|
|
196
|
+
|
|
197
|
+
if prompt is None and image is None:
|
|
198
|
+
raise excs.Error('At least one of `prompt` or `image` must be provided.')
|
|
199
|
+
|
|
200
|
+
image_: Optional[types.Image] = None
|
|
201
|
+
if image is not None:
|
|
202
|
+
with io.BytesIO() as buffer:
|
|
203
|
+
image.save(buffer, format='jpeg')
|
|
204
|
+
image_ = types.Image(image_bytes=buffer.getvalue(), mime_type='image/jpeg')
|
|
205
|
+
|
|
206
|
+
config_ = types.GenerateVideosConfig(**config) if config else None
|
|
207
|
+
operation = await _genai_client().aio.models.generate_videos(
|
|
208
|
+
model=model, prompt=prompt, image=image_, config=config_
|
|
209
|
+
)
|
|
210
|
+
while not operation.done:
|
|
211
|
+
await asyncio.sleep(3)
|
|
212
|
+
operation = await _genai_client().aio.operations.get(operation)
|
|
213
|
+
|
|
214
|
+
video = operation.response.generated_videos[0]
|
|
215
|
+
|
|
216
|
+
video_bytes = await _genai_client().aio.files.download(file=video.video) # type: ignore[arg-type]
|
|
217
|
+
assert video_bytes is not None
|
|
218
|
+
|
|
219
|
+
_, output_filename = tempfile.mkstemp(suffix='.mp4', dir=str(env.Env.get().tmp_dir))
|
|
220
|
+
Path(output_filename).write_bytes(video_bytes)
|
|
221
|
+
return output_filename
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@generate_videos.resource_pool
|
|
225
|
+
def _(model: str) -> str:
|
|
226
|
+
return f'request-rate:veo:{model}'
|
pixeltable/functions/globals.py
CHANGED
|
@@ -49,22 +49,7 @@ def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
|
|
|
49
49
|
allows_window=True,
|
|
50
50
|
# Allow counting non-null values of any type
|
|
51
51
|
# TODO: should we have an "Any" type that can be used here?
|
|
52
|
-
type_substitutions=tuple(
|
|
53
|
-
{T: Optional[t]} # type: ignore[misc]
|
|
54
|
-
for t in (
|
|
55
|
-
ts.String,
|
|
56
|
-
ts.Int,
|
|
57
|
-
ts.Float,
|
|
58
|
-
ts.Bool,
|
|
59
|
-
ts.Timestamp,
|
|
60
|
-
ts.Array,
|
|
61
|
-
ts.Json,
|
|
62
|
-
ts.Image,
|
|
63
|
-
ts.Video,
|
|
64
|
-
ts.Audio,
|
|
65
|
-
ts.Document,
|
|
66
|
-
)
|
|
67
|
-
),
|
|
52
|
+
type_substitutions=tuple({T: Optional[t]} for t in ts.ALL_PIXELTABLE_TYPES), # type: ignore[misc]
|
|
68
53
|
)
|
|
69
54
|
class count(func.Aggregator, typing.Generic[T]):
|
|
70
55
|
def __init__(self) -> None:
|
pixeltable/functions/json.py
CHANGED
|
@@ -4,9 +4,10 @@ Pixeltable [UDFs](https://pixeltable.readme.io/docs/user-defined-functions-udfs)
|
|
|
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(
|
|
10
|
+
t.select(pxtf.json.make_list(t.json_col)).collect()
|
|
10
11
|
```
|
|
11
12
|
"""
|
|
12
13
|
|