Skip to content

Commit

Permalink
chore: add Feature creation
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 635593604
  • Loading branch information
vertex-sdk-bot authored and Copybara-Service committed May 20, 2024
1 parent 47262ef commit 352eccf
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
79 changes: 79 additions & 0 deletions tests/unit/vertexai/test_feature_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
_TEST_FG3_ENTITY_ID_COLUMNS,
_TEST_FG3_LABELS,
_TEST_FG_LIST,
_TEST_FG1_F1,
_TEST_FG1_F1_ID,
_TEST_FG1_F1_PATH,
_TEST_FG1_F1_DESCRIPTION,
Expand Down Expand Up @@ -125,6 +126,18 @@ def delete_fg_mock():
yield delete_fg_mock


@pytest.fixture
def create_feature_mock():
with patch.object(
feature_registry_service_client.FeatureRegistryServiceClient,
"create_feature",
) as create_feature_mock:
create_feature_lro_mock = mock.Mock(ga_operation.Operation)
create_feature_lro_mock.result.return_value = _TEST_FG1_F1
create_feature_mock.return_value = create_feature_lro_mock
yield create_feature_mock


def fg_eq(
fg_to_check: FeatureGroup,
name: str,
Expand Down Expand Up @@ -363,3 +376,69 @@ def test_get_feature(get_fg_mock, get_feature_mock):
labels=_TEST_FG1_F1_LABELS,
point_of_contact=_TEST_FG1_F1_POINT_OF_CONTACT,
)


@pytest.mark.parametrize("create_request_timeout", [None, 1.0])
@pytest.mark.parametrize("sync", [True, False])
def test_create_feature(
get_fg_mock,
create_feature_mock,
get_feature_mock,
fg_logger_mock,
create_request_timeout,
sync,
):
aiplatform.init(project=_TEST_PROJECT, location=_TEST_LOCATION)

fg = FeatureGroup(_TEST_FG1_ID)
feature = fg.create_feature(
_TEST_FG1_F1_ID,
description=_TEST_FG1_F1_DESCRIPTION,
labels=_TEST_FG1_F1_LABELS,
point_of_contact=_TEST_FG1_F1_POINT_OF_CONTACT,
create_request_timeout=create_request_timeout,
sync=sync,
)

if not sync:
feature.wait()

expected_feature = types.feature.Feature(
description=_TEST_FG1_F1_DESCRIPTION,
labels=_TEST_FG1_F1_LABELS,
point_of_contact=_TEST_FG1_F1_POINT_OF_CONTACT,
)
create_feature_mock.assert_called_once_with(
parent=_TEST_FG1_PATH,
feature=expected_feature,
feature_id=_TEST_FG1_F1_ID,
metadata=(),
timeout=create_request_timeout,
)

feature_eq(
feature,
name=_TEST_FG1_F1_ID,
resource_name=_TEST_FG1_F1_PATH,
project=_TEST_PROJECT,
location=_TEST_LOCATION,
description=_TEST_FG1_F1_DESCRIPTION,
labels=_TEST_FG1_F1_LABELS,
point_of_contact=_TEST_FG1_F1_POINT_OF_CONTACT,
)

fg_logger_mock.assert_has_calls(
[
call("Creating Feature"),
call(
f"Create Feature backing LRO: {create_feature_mock.return_value.operation.name}"
),
call(
"Feature created. Resource name: projects/test-project/locations/us-central1/featureGroups/my_fg1/features/my_fg1_f1"
),
call("To use this Feature in another session:"),
call(
"feature = aiplatform.Feature('projects/test-project/locations/us-central1/featureGroups/my_fg1/features/my_fg1_f1')"
),
]
)
106 changes: 106 additions & 0 deletions vertexai/resources/preview/feature_store/feature_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from google.cloud.aiplatform import base, initializer
from google.cloud.aiplatform import utils
from google.cloud.aiplatform.compat.types import (
feature as gca_feature,
feature_group as gca_feature_group,
io as gca_io,
)
Expand Down Expand Up @@ -252,6 +253,111 @@ def get_feature(self, feature_id: str) -> Feature:
"""
return Feature(f"{self.resource_name}/features/{feature_id}")

def create_feature(
self,
name: str,
version_column_name: Optional[str] = None,
description: Optional[str] = None,
labels: Optional[Dict[str, str]] = None,
point_of_contact: Optional[str] = None,
project: Optional[str] = None,
location: Optional[str] = None,
credentials: Optional[auth_credentials.Credentials] = None,
request_metadata: Optional[Sequence[Tuple[str, str]]] = None,
create_request_timeout: Optional[float] = None,
sync: bool = True,
) -> Feature:
"""Creates a new feature.
Args:
name: The name of the feature.
version_column_name:
The name of the BigQuery Table/View column hosting data for this
version. If no value is provided, will use feature_id.
description: Description of the feature.
labels:
The labels with user-defined metadata to organize your Features.
Label keys and values can be no longer than 64 characters
(Unicode codepoints), can only contain lowercase letters,
numeric characters, underscores and dashes. International
characters are allowed.
See https://goo.gl/xmQnxf for more information on and examples
of labels. No more than 64 user labels can be associated with
one Feature (System labels are excluded)." System reserved label
keys are prefixed with "aiplatform.googleapis.com/" and are
immutable.
point_of_contact:
Entity responsible for maintaining this feature. Can be comma
separated list of email addresses or URIs.
project:
Project to create feature in. If unset, the project set in
aiplatform.init will be used.
location:
Location to create feature in. If not set, location set in
aiplatform.init will be used.
credentials:
Custom credentials to use to create this feature. Overrides
credentials set in aiplatform.init.
request_metadata:
Strings which should be sent along with the request as metadata.
create_request_timeout:
The timeout for the create request in seconds.
sync:
Whether to execute this creation synchronously. If False, this
method will be executed in concurrent Future and any downstream
object will be immediately returned and synced when the Future
has completed.
Returns:
Feature - the Feature resource object.
"""

gapic_feature = gca_feature.Feature()

if version_column_name:
gapic_feature.version_column_name = version_column_name

if description:
gapic_feature.description = description

if labels:
utils.validate_labels(labels)
gapic_feature.labels = labels

if point_of_contact:
gapic_feature.point_of_contact = point_of_contact

if request_metadata is None:
request_metadata = ()

api_client = self.__class__._instantiate_client(
location=location, credentials=credentials
)

create_feature_lro = api_client.create_feature(
parent=self.resource_name,
feature=gapic_feature,
feature_id=name,
metadata=request_metadata,
timeout=create_request_timeout,
)

_LOGGER.log_create_with_lro(Feature, create_feature_lro)

created_feature = create_feature_lro.result()

_LOGGER.log_create_complete(Feature, created_feature, "feature")

feature_obj = Feature(
name=created_feature.name,
project=project,
location=location,
credentials=credentials,
)

return feature_obj

@property
def source(self) -> FeatureGroupBigQuerySource:
return FeatureGroupBigQuerySource(
Expand Down

0 comments on commit 352eccf

Please sign in to comment.