Skip to content

Commit 0abe0b7

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
feat: add vertex_registry_source to create_feature_view method
PiperOrigin-RevId: 724463720
1 parent 1624a23 commit 0abe0b7

11 files changed

+421
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START aiplatform_sdk_create_feature_view_from_registry_source]
16+
17+
from google.cloud import aiplatform
18+
from vertexai.resources.preview import feature_store
19+
from typing import List, Optional
20+
21+
22+
def create_feature_view_from_registry_source(
23+
project: str,
24+
location: str,
25+
existing_feature_online_store_id: str,
26+
feature_view_id: str,
27+
features: List[str],
28+
project_number: Optional[int],
29+
):
30+
aiplatform.init(project=project, location=location)
31+
fos = feature_store.FeatureOnlineStore(existing_feature_online_store_id)
32+
fv = fos.create_feature_view(
33+
name=feature_view_id,
34+
source=feature_store.utils.FeatureViewRegistrySource(
35+
features=features,
36+
project_number=project_number,
37+
),
38+
)
39+
return fv
40+
41+
42+
# [END aiplatform_sdk_create_feature_view_from_registry_source]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from feature_store import create_feature_view_from_registry_source
16+
import test_constants as constants
17+
18+
19+
def test_create_feature_view_from_registry_source_sample(
20+
mock_sdk_init,
21+
mock_get_feature_online_store,
22+
):
23+
create_feature_view_from_registry_source.create_feature_view_from_registry_source(
24+
project=constants.PROJECT,
25+
location=constants.LOCATION,
26+
existing_feature_online_store_id=constants.FEATURE_ONLINE_STORE_ID,
27+
feature_view_id=constants.FEATURE_VIEW_ID,
28+
features=[
29+
constants.FR_FEATURE_ID,
30+
constants.FR_FEATURE_ID_2,
31+
constants.FR_FEATURE_ID_3,
32+
constants.FR_FEATURE_ID_4,
33+
],
34+
project_number=constants.PROJECT_NUMBER,
35+
)
36+
37+
mock_sdk_init.assert_called_once_with(
38+
project=constants.PROJECT, location=constants.LOCATION
39+
)
40+
41+
mock_get_feature_online_store.assert_called_once()
42+
mock_get_feature_online_store.return_value.create_feature_view.assert_called_once_with(
43+
name=constants.FEATURE_VIEW_ID,
44+
source=constants.FEATURE_VIEW_REGISTRY_SOURCE,
45+
)

samples/model-builder/test_constants.py

+28-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import vertexai
2222

2323
PROJECT = "abc"
24+
PROJECT_NUMBER = 123
2425
LOCATION = "us-central1"
2526
LOCATION_EUROPE = "europe-west4"
2627
LOCATION_ASIA = "asia-east1"
@@ -280,6 +281,7 @@
280281
)
281282
)
282283
FEATURE_GROUP_ID = "sample_feature_group"
284+
FEATURE_GROUP_ID_2 = "sample_feature_group_2"
283285
FEATURE_GROUP_BQ_URI = "bq://my_proj.my_dataset.my_table"
284286
FEATURE_GROUP_BQ_ENTITY_ID_COLUMNS = ["id"]
285287
FEATURE_GROUP_SOURCE = (
@@ -289,8 +291,28 @@
289291
)
290292
)
291293
REGISTRY_FEATURE_ID = "sample_feature"
294+
REGISTRY_FEATURE_ID_2 = "sample_feature_2"
295+
FG_2_REGISTRY_FEATURE_ID_3 = "sample_feature_3"
296+
FG_2_REGISTRY_FEATURE_ID_4 = "sample_feature_4"
292297
VERSION_COLUMN_NAME = "feature_column"
293298
PROJECT_ALLOWLISTED = ["test-project"]
299+
FR_FEATURE_ID = ".".join([FEATURE_GROUP_ID, REGISTRY_FEATURE_ID])
300+
FR_FEATURE_ID_2 = ".".join([FEATURE_GROUP_ID, REGISTRY_FEATURE_ID_2])
301+
FR_FEATURE_ID_3 = ".".join([FEATURE_GROUP_ID_2, FG_2_REGISTRY_FEATURE_ID_3])
302+
FR_FEATURE_ID_4 = ".".join([FEATURE_GROUP_ID_2, FG_2_REGISTRY_FEATURE_ID_4])
303+
304+
FEATURE_GROUPS_MAPPING = {
305+
FEATURE_GROUP_ID: [REGISTRY_FEATURE_ID, REGISTRY_FEATURE_ID_2],
306+
FEATURE_GROUP_ID_2: [FG_2_REGISTRY_FEATURE_ID_3, FG_2_REGISTRY_FEATURE_ID_4],
307+
}
308+
309+
310+
FEATURE_VIEW_REGISTRY_SOURCE = (
311+
vertexai.resources.preview.feature_store.utils.FeatureViewRegistrySource(
312+
features=[FR_FEATURE_ID, FR_FEATURE_ID_2, FR_FEATURE_ID_3, FR_FEATURE_ID_4],
313+
project_number=PROJECT_NUMBER,
314+
)
315+
)
294316

295317
TABULAR_TARGET_COLUMN = "target_column"
296318
FORECASTNG_TIME_COLUMN = "date"
@@ -382,8 +404,12 @@
382404
# Vector Search
383405
VECTOR_SEARCH_INDEX = "123"
384406
VECTOR_SEARCH_INDEX_DATAPOINTS = [
385-
aiplatform.compat.types.index_v1beta1.IndexDatapoint(datapoint_id="datapoint_id_1", feature_vector=[0.1, 0.2]),
386-
aiplatform.compat.types.index_v1beta1.IndexDatapoint(datapoint_id="datapoint_id_2", feature_vector=[0.3, 0.4]),
407+
aiplatform.compat.types.index_v1beta1.IndexDatapoint(
408+
datapoint_id="datapoint_id_1", feature_vector=[0.1, 0.2]
409+
),
410+
aiplatform.compat.types.index_v1beta1.IndexDatapoint(
411+
datapoint_id="datapoint_id_2", feature_vector=[0.3, 0.4]
412+
),
387413
]
388414
VECTOR_SEARCH_INDEX_DATAPOINT_IDS = ["datapoint_id_1", "datapoint_id_2"]
389415
VECTOR_SEARCH_INDEX_ENDPOINT = "456"

tests/unit/vertexai/conftest.py

+23
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
_TEST_FV_LIST,
6767
_TEST_FV1,
6868
_TEST_FV3,
69+
_TEST_FV4,
6970
_TEST_OPTIMIZED_EMBEDDING_FV,
7071
_TEST_OPTIMIZED_FV1,
7172
_TEST_OPTIMIZED_FV2,
@@ -444,6 +445,16 @@ def get_rag_fv_mock():
444445
yield get_rag_fv_mock
445446

446447

448+
@pytest.fixture
449+
def get_registry_fv_mock():
450+
with patch.object(
451+
feature_online_store_admin_service_client.FeatureOnlineStoreAdminServiceClient,
452+
"get_feature_view",
453+
) as get_rag_fv_mock:
454+
get_rag_fv_mock.return_value = _TEST_FV4
455+
yield get_rag_fv_mock
456+
457+
447458
@pytest.fixture
448459
def list_fv_mock():
449460
with patch.object(
@@ -478,6 +489,18 @@ def create_rag_fv_mock():
478489
yield create_rag_fv_mock
479490

480491

492+
@pytest.fixture
493+
def create_registry_fv_mock():
494+
with patch.object(
495+
feature_online_store_admin_service_client.FeatureOnlineStoreAdminServiceClient,
496+
"create_feature_view",
497+
) as create_registry_fv_mock:
498+
create_registry_fv_lro_mock = mock.Mock(ga_operation.Operation)
499+
create_registry_fv_lro_mock.result.return_value = _TEST_FV4
500+
create_registry_fv_mock.return_value = create_registry_fv_lro_mock
501+
yield create_registry_fv_mock
502+
503+
481504
@pytest.fixture
482505
def create_embedding_fv_from_bq_mock():
483506
with patch.object(

tests/unit/vertexai/feature_store_constants.py

+25-2
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,6 @@
169169
)
170170

171171

172-
_TEST_FV_LIST = [_TEST_FV1, _TEST_FV2, _TEST_FV3]
173-
174172
# Test feature view sync 1
175173
_TEST_FV_SYNC1_ID = "my_fv_sync1"
176174
_TEST_FV_SYNC1_PATH = f"{_TEST_FV1_PATH}/featureViewSyncs/my_fv_sync1"
@@ -300,6 +298,8 @@
300298

301299

302300
_TEST_FG2_ID = "my_fg2"
301+
_TEST_FG2_F1_ID = "my_fg2_f1"
302+
_TEST_FG2_F2_ID = "my_fg2_f2"
303303
_TEST_FG2_PATH = f"{_TEST_PARENT}/featureGroups/{_TEST_FG2_ID}"
304304
_TEST_FG2_BQ_URI = f"bq://{_TEST_PROJECT}.my_dataset.my_table_for_fg2"
305305
_TEST_FG2_ENTITY_ID_COLUMNS = ["entity_id1", "entity_id2"]
@@ -475,3 +475,26 @@
475475
point_of_contact=_TEST_FG1_F1_POINT_OF_CONTACT,
476476
feature_stats_and_anomaly=[_TEST_FG1_F1_FEATURE_STATS_AND_ANOMALY],
477477
)
478+
479+
# Test feature view 4
480+
_TEST_FV4_ID = "my_fv4"
481+
_TEST_FV4_PATH = f"{_TEST_BIGTABLE_FOS1_PATH}/featureViews/my_fv4"
482+
_TEST_FV4_LABELS = {"my_key": "my_fv4"}
483+
_TEST_FV4 = types.feature_view.FeatureView(
484+
name=_TEST_FV4_PATH,
485+
feature_registry_source=types.feature_view.FeatureView.FeatureRegistrySource(
486+
feature_groups=[
487+
types.feature_view.FeatureView.FeatureRegistrySource.FeatureGroup(
488+
feature_group_id=_TEST_FG1_ID,
489+
feature_ids=[_TEST_FG1_F1_ID, _TEST_FG1_F2_ID],
490+
),
491+
types.feature_view.FeatureView.FeatureRegistrySource.FeatureGroup(
492+
feature_group_id=_TEST_FG2_ID,
493+
feature_ids=[_TEST_FG2_F1_ID, _TEST_FG2_F2_ID],
494+
),
495+
],
496+
),
497+
labels=_TEST_FV4_LABELS,
498+
)
499+
500+
_TEST_FV_LIST = [_TEST_FV1, _TEST_FV2, _TEST_FV3, _TEST_FV4]

0 commit comments

Comments
 (0)