Skip to content

feat: publish bigframes blob(Multimodal) to preview #1693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bigframes/_config/display_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class DisplayOptions:
max_info_rows: Optional[int] = 200000
memory_usage: bool = True

blob_display: bool = True
blob_display_width: Optional[int] = None
blob_display_height: Optional[int] = None


@contextlib.contextmanager
def pandas_repr(display_options: DisplayOptions):
Expand Down
64 changes: 46 additions & 18 deletions bigframes/_config/experiment_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing import Optional
import warnings

import bigframes
import bigframes.exceptions as bfe


Expand All @@ -26,10 +27,6 @@ class ExperimentOptions:
def __init__(self):
self._semantic_operators: bool = False
self._ai_operators: bool = False
self._blob: bool = False
self._blob_display: bool = True
self._blob_display_width: Optional[int] = None
self._blob_display_height: Optional[int] = None

@property
def semantic_operators(self) -> bool:
Expand Down Expand Up @@ -60,41 +57,72 @@ def ai_operators(self, value: bool):

@property
def blob(self) -> bool:
return self._blob
msg = bfe.format_message(
"BigFrames Blob is in preview now. This flag is no longer needed."
)
warnings.warn(msg, category=bfe.ApiDeprecationWarning)
return True

@blob.setter
def blob(self, value: bool):
if value is True:
msg = bfe.format_message(
"BigFrames Blob is still under experiments. It may not work and "
"subject to change in the future."
)
warnings.warn(msg, category=bfe.PreviewWarning)
self._blob = value
msg = bfe.format_message(
"BigFrames Blob is in preview now. This flag is no longer needed."
)
warnings.warn(msg, category=bfe.ApiDeprecationWarning)

@property
def blob_display(self) -> bool:
"""Whether to display the blob content in notebook DataFrame preview. Default True."""
return self._blob_display
msg = bfe.format_message(
"BigFrames Blob is in preview now. The option has been moved to bigframes.options.display.blob_display."
)
warnings.warn(msg, category=bfe.ApiDeprecationWarning)

return bigframes.options.display.blob_display

@blob_display.setter
def blob_display(self, value: bool):
self._blob_display = value
msg = bfe.format_message(
"BigFrames Blob is in preview now. The option has been moved to bigframes.options.display.blob_display."
)
warnings.warn(msg, category=bfe.ApiDeprecationWarning)

bigframes.options.display.blob_display = value

@property
def blob_display_width(self) -> Optional[int]:
"""Width in pixels that the blob constrained to."""
return self._blob_display_width
msg = bfe.format_message(
"BigFrames Blob is in preview now. The option has been moved to bigframes.options.display.blob_display_width."
)
warnings.warn(msg, category=bfe.ApiDeprecationWarning)

return bigframes.options.display.blob_display_width

@blob_display_width.setter
def blob_display_width(self, value: Optional[int]):
self._blob_display_width = value
msg = bfe.format_message(
"BigFrames Blob is in preview now. The option has been moved to bigframes.options.display.blob_display_width."
)
warnings.warn(msg, category=bfe.ApiDeprecationWarning)

bigframes.options.display.blob_display_width = value

@property
def blob_display_height(self) -> Optional[int]:
"""Height in pixels that the blob constrained to."""
return self._blob_display_height
msg = bfe.format_message(
"BigFrames Blob is in preview now. The option has been moved to bigframes.options.display.blob_display_height."
)
warnings.warn(msg, category=bfe.ApiDeprecationWarning)

return bigframes.options.display.blob_display_height

@blob_display_height.setter
def blob_display_height(self, value: Optional[int]):
self._blob_display_height = value
msg = bfe.format_message(
"BigFrames Blob is in preview now. The option has been moved to bigframes.options.display.blob_display_height."
)
warnings.warn(msg, category=bfe.ApiDeprecationWarning)

bigframes.options.display.blob_display_height = value
24 changes: 9 additions & 15 deletions bigframes/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,14 +768,11 @@ def _repr_html_(self) -> str:
return formatter.repr_query_job(self._compute_dry_run())

df = self.copy()
if (
bigframes.options.experiments.blob
and bigframes.options.experiments.blob_display
):
if bigframes.options.display.blob_display:
blob_cols = [
col
for col in df.columns
if df[col].dtype == bigframes.dtypes.OBJ_REF_DTYPE
series_name
for series_name, series in df.items()
if series.dtype == bigframes.dtypes.OBJ_REF_DTYPE
]
for col in blob_cols:
# TODO(garrettwu): Not necessary to get access urls for all the rows. Update when having a to get URLs from local data.
Expand All @@ -794,10 +791,7 @@ def _repr_html_(self) -> str:

with display_options.pandas_repr(opts):
# Allows to preview images in the DataFrame. The implementation changes the string repr as well, that it doesn't truncate strings or escape html charaters such as "<" and ">". We may need to implement a full-fledged repr module to better support types not in pandas.
if (
bigframes.options.experiments.blob
and bigframes.options.experiments.blob_display
):
if bigframes.options.display.blob_display and blob_cols:

def obj_ref_rt_to_html(obj_ref_rt) -> str:
obj_ref_rt_json = json.loads(obj_ref_rt)
Expand All @@ -809,12 +803,12 @@ def obj_ref_rt_to_html(obj_ref_rt) -> str:
)
if content_type.startswith("image"):
size_str = ""
if bigframes.options.experiments.blob_display_width:
size_str = f' width="{bigframes.options.experiments.blob_display_width}"'
if bigframes.options.experiments.blob_display_height:
if bigframes.options.display.blob_display_width:
size_str = f' width="{bigframes.options.display.blob_display_width}"'
if bigframes.options.display.blob_display_height:
size_str = (
size_str
+ f' height="{bigframes.options.experiments.blob_display_height}"'
+ f' height="{bigframes.options.display.blob_display_height}"'
)
url = obj_ref_rt_json["access_urls"]["read_url"]
return f'<img src="{url}"{size_str}>'
Expand Down
15 changes: 8 additions & 7 deletions bigframes/ml/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,10 @@ class MultimodalEmbeddingGenerator(base.RetriableRemotePredictor):
"""Multimodal embedding generator LLM model.

.. note::
BigFrames Blob is still under experiments. It may not work and subject to change in the future.
BigFrames Blob is subject to the "Pre-GA Offerings Terms" in the General Service Terms section of the
Service Specific Terms(https://cloud.google.com/terms/service-terms#1). Pre-GA products and features are available "as is"
and might have limited support. For more information, see the launch stage descriptions
(https://cloud.google.com/products#product-launch-stages).

Args:
model_name (str, Default to "multimodalembedding@001"):
Expand All @@ -271,8 +274,6 @@ def __init__(
session: Optional[bigframes.Session] = None,
connection_name: Optional[str] = None,
):
if not bigframes.options.experiments.blob:
raise NotImplementedError()
if model_name is None:
model_name = "multimodalembedding@001"
msg = exceptions.format_message(_REMOVE_DEFAULT_MODEL_WARNING)
Expand Down Expand Up @@ -610,7 +611,10 @@ def predict(

prompt (Iterable of str or bigframes.series.Series, or None, default None):
.. note::
BigFrames Blob is still under experiments. It may not work and subject to change in the future.
BigFrames Blob is subject to the "Pre-GA Offerings Terms" in the General Service Terms section of the
Service Specific Terms(https://cloud.google.com/terms/service-terms#1). Pre-GA products and features are available "as is"
and might have limited support. For more information, see the launch stage descriptions
(https://cloud.google.com/products#product-launch-stages).

Construct a prompt struct column for prediction based on the input. The input must be an Iterable that can take string literals,
such as "summarize", string column(s) of X, such as X["str_col"], or blob column(s) of X, such as X["blob_col"].
Expand Down Expand Up @@ -646,9 +650,6 @@ def predict(
(X,) = utils.batch_convert_to_dataframe(X, session=session)

if prompt:
if not bigframes.options.experiments.blob:
raise NotImplementedError()

if self.model_name not in _GEMINI_MULTIMODAL_ENDPOINTS:
raise NotImplementedError(
f"GeminiTextGenerator only supports model_name {', '.join(_GEMINI_MULTIMODAL_ENDPOINTS)} for Multimodal prompt."
Expand Down
Loading