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.

Files changed (50) hide show
  1. pixeltable/__init__.py +2 -2
  2. pixeltable/__version__.py +2 -2
  3. pixeltable/catalog/catalog.py +9 -7
  4. pixeltable/catalog/column.py +6 -2
  5. pixeltable/catalog/dir.py +2 -1
  6. pixeltable/catalog/insertable_table.py +1 -1
  7. pixeltable/catalog/schema_object.py +2 -1
  8. pixeltable/catalog/table.py +12 -8
  9. pixeltable/catalog/table_version.py +21 -0
  10. pixeltable/catalog/view.py +3 -3
  11. pixeltable/dataframe.py +48 -5
  12. pixeltable/env.py +1 -1
  13. pixeltable/exec/aggregation_node.py +14 -0
  14. pixeltable/exec/cache_prefetch_node.py +1 -1
  15. pixeltable/exec/expr_eval/expr_eval_node.py +1 -1
  16. pixeltable/exprs/column_ref.py +42 -17
  17. pixeltable/exprs/data_row.py +3 -0
  18. pixeltable/exprs/globals.py +1 -1
  19. pixeltable/exprs/literal.py +11 -1
  20. pixeltable/exprs/rowid_ref.py +4 -1
  21. pixeltable/exprs/similarity_expr.py +1 -1
  22. pixeltable/func/function.py +1 -1
  23. pixeltable/functions/__init__.py +1 -0
  24. pixeltable/functions/date.py +185 -0
  25. pixeltable/functions/gemini.py +184 -49
  26. pixeltable/functions/globals.py +1 -16
  27. pixeltable/functions/json.py +2 -1
  28. pixeltable/functions/math.py +103 -0
  29. pixeltable/functions/string.py +1 -2
  30. pixeltable/functions/video.py +2 -2
  31. pixeltable/globals.py +26 -9
  32. pixeltable/io/hf_datasets.py +2 -2
  33. pixeltable/io/pandas.py +16 -4
  34. pixeltable/io/parquet.py +4 -2
  35. pixeltable/metadata/__init__.py +1 -1
  36. pixeltable/metadata/converters/convert_34.py +21 -0
  37. pixeltable/metadata/notes.py +1 -0
  38. pixeltable/plan.py +12 -5
  39. pixeltable/share/__init__.py +1 -1
  40. pixeltable/share/packager.py +397 -120
  41. pixeltable/share/publish.py +61 -16
  42. pixeltable/store.py +57 -20
  43. pixeltable/type_system.py +46 -2
  44. pixeltable/utils/arrow.py +8 -2
  45. pixeltable/utils/pytorch.py +4 -0
  46. {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/METADATA +2 -4
  47. {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/RECORD +50 -48
  48. {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/LICENSE +0 -0
  49. {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/WHEEL +0 -0
  50. {pixeltable-0.3.13.dist-info → pixeltable-0.3.15.dist-info}/entry_points.txt +0 -0
@@ -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.TIMESTAMP.name:
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)
@@ -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 = ColumnRef.find_embedding_index(col_ref.col, idx_name, 'similarity')
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
@@ -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 serlialized, so there is no corresponding
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
@@ -6,6 +6,7 @@ from . import (
6
6
  anthropic,
7
7
  audio,
8
8
  bedrock,
9
+ date,
9
10
  deepseek,
10
11
  fireworks,
11
12
  gemini,
@@ -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__
@@ -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-generativeai` and configure your Gemini credentials, as described in
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
- from typing import Optional
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) -> None:
16
- import google.generativeai as genai
24
+ def _(api_key: str) -> 'genai.client.Client':
25
+ from google import genai
17
26
 
18
- genai.configure(api_key=api_key)
27
+ return genai.client.Client(api_key=api_key)
19
28
 
20
29
 
21
- def _ensure_loaded() -> None:
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-generativeai`
48
+ - `pip install google-genai`
52
49
 
53
50
  Args:
54
51
  contents: The input content to generate from.
55
- model_name: The name of the model to use.
56
-
57
- For details on the other parameters, see: <https://ai.google.dev/gemini-api/docs>
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-1.5-flash`
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, model_name='gemini-1.5-flash'))
66
+ >>> tbl.add_computed_column(response=generate_content(tbl.prompt, model='gemini-2.0-flash'))
67
67
  """
68
- env.Env.get().require_package('google.generativeai')
69
- _ensure_loaded()
70
- import google.generativeai as genai
71
-
72
- model = genai.GenerativeModel(model_name=model_name)
73
- gc = genai.GenerationConfig(
74
- candidate_count=candidate_count,
75
- stop_sequences=stop_sequences,
76
- max_output_tokens=max_output_tokens,
77
- temperature=temperature,
78
- top_p=top_p,
79
- top_k=top_k,
80
- response_mime_type=response_mime_type,
81
- response_schema=response_schema,
82
- presence_penalty=presence_penalty,
83
- frequency_penalty=frequency_penalty,
84
- )
85
- response = await model.generate_content_async(contents, generation_config=gc)
86
- return response.to_dict()
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 _(model_name: str) -> str:
91
- return f'request-rate:gemini:{model_name}'
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}'
@@ -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:
@@ -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(pxt.functions.json.make_list()).collect()
10
+ t.select(pxtf.json.make_list(t.json_col)).collect()
10
11
  ```
11
12
  """
12
13