Skip to content

Commit 5231bac

Browse files
Draft changes to add remote online store to feast.
Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> Adding the integration test and remote online creator class so that it will fit into existing integration testing framework. Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> Fix after rebase Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> Removing the RemoteOnlineStoreCreator and adding custom integration test case. Incorporating the code review comments. Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> reformatting the code, removing unnecessary braces. Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> Trying to fix the errors reported in make lint-python Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> Ran the command make format-python and trying to see if it fixes the lint errors. Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> increasing the server start timeout to see if it fixes the integration test cases. Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> checking changes after make format-python Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> trying to see if this fixes the PR integrationt test failure. Signed-off-by: Lokesh Rangineni <lokeshemail@email.com> Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> checking in the changes for make format-python Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> Upgrading python version to 3.11, adding support for 3.11 as well. Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> chore: Bump macOS runners to macos-13 (feast-dev#4152) bump macos runner to 13 Signed-off-by: tokoko <togurg14@freeuni.edu.ge> Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> chore: Use pixi to lock python dependencies in a single command (feast-dev#4114) use pixi to lock python dependencies in a single command Signed-off-by: tokoko <togurg14@freeuni.edu.ge> Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> feat: List all feature views (feast-dev#4256) * feature: Adding type to base feature view Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fixed linter Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fixed type and meta Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * adding new listing Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * cleaning up changes Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * reverting FV proto Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * doing simple way Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * added a test Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated to add warnings Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> --------- Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> feat: Adding vector search for sqlite (feast-dev#4176) * feat: Adding vector search for sqlite Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * adding the sqlite_vss dependency Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * linter Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * latest progress Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * uploading latest progress Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated function Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * adding configuration Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * adding current progress Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updating requirements files Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * moving to sqlite-vec Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updating sqlite.py Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * checking in progress Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated test type Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * got the initialization working, nice Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * checking in progress from last night Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * removing unnecessary stuff Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fixing merge conflicts Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * removing files changed accidentally] Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * uploading current progress...things run but need to update the virtual table insertion Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * linted Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * adding working notes Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * found a bug, original feature_store.py was only grabbing first feature view, adjusted Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * cant use a string have to verify it is a proper FeatureView object Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated got it working, need to fix some other stuff still Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * working Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * linter Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fixing some type issues Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fixed typing and lint issues Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated dependencies Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fix for pixi and updating requirements Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fixed type Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * linter Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * testing sqlite_vec import Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * adding minimal example test Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * lint Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * testing raw sqlite Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * Printing package version * printing version Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated requirements * rebuilding requirments Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * only going to run this on 3.10 for now Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated docs for sqlite caveats Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * adding reason Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * skipping Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated tests Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * removing print Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * added method call Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * added prubt Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * added print Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * removing print Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * adding check in sqlite Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * missed an = Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * still running on 3.11 Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * typo Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fix Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * fix Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * updated setup and docs Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> * renamed things Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> --------- Signed-off-by: Francisco Javier Arceo <farceo@redhat.com> squashing the last 15 commits to one. Merge branch 'master' into feature/adding-remote-onlinestore-rebase Adding documentation and incorporating code review comment. Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> Adding documentation and incorporating code review comment. Signed-off-by: Lokesh Rangineni <lokeshforjava@gmail.com> Merge remote-tracking branch 'fork/feature/adding-remote-onlinestore-rebase' into feature/adding-remote-onlinestore-rebase
1 parent 0d162e9 commit 5231bac

26 files changed

+1465
-421
lines changed

.github/workflows/pr_integration_tests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ jobs:
8686
strategy:
8787
fail-fast: false
8888
matrix:
89-
python-version: [ "3.9", "3.10", "3.11" ]
89+
python-version: [ "3.11" ]
9090
os: [ ubuntu-latest ]
9191
env:
9292
OS: ${{ matrix.os }}

Makefile

-6
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,6 @@ lock-python-dependencies-all:
7070
pixi run --environment py311 --manifest-path infra/scripts/pixi/pixi.toml "uv pip compile --system --no-strip-extras setup.py --output-file sdk/python/requirements/py3.11-requirements.txt"
7171
pixi run --environment py311 --manifest-path infra/scripts/pixi/pixi.toml "uv pip compile --system --no-strip-extras setup.py --extra ci --output-file sdk/python/requirements/py3.11-ci-requirements.txt"
7272

73-
lock-python-dependencies-all:
74-
pixi run --environment py39 --manifest-path infra/scripts/pixi/pixi.toml "python -m piptools compile -U --output-file sdk/python/requirements/py3.9-requirements.txt"
75-
pixi run --environment py39 --manifest-path infra/scripts/pixi/pixi.toml "python -m piptools compile -U --extra ci --output-file sdk/python/requirements/py3.9-ci-requirements.txt"
76-
pixi run --environment py310 --manifest-path infra/scripts/pixi/pixi.toml "python -m piptools compile -U --output-file sdk/python/requirements/py3.10-requirements.txt"
77-
pixi run --environment py310 --manifest-path infra/scripts/pixi/pixi.toml "python -m piptools compile -U --extra ci --output-file sdk/python/requirements/py3.10-ci-requirements.txt"
78-
7973
benchmark-python:
8074
IS_TEST=True python -m pytest --integration --benchmark --benchmark-autosave --benchmark-save-data sdk/python/tests
8175

docs/reference/alpha-vector-database.md

+18
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ Below are supported vector databases and implemented features:
1313
| Elasticsearch | [x] | [x] |
1414
| Milvus | [ ] | [ ] |
1515
| Faiss | [ ] | [ ] |
16+
| SQLite | [x] | [ ] |
1617

18+
Note: SQLite is in limited access and only working on Python 3.10. It will be updated as [sqlite_vec](https://github.com/asg017/sqlite-vec/) progresses.
1719

1820
## Example
1921

@@ -108,4 +110,20 @@ def print_online_features(features):
108110
print(key, " : ", value)
109111

110112
print_online_features(features)
113+
```
114+
115+
### Configuration
116+
We offer two Online Store options for Vector Databases. PGVector and SQLite.
117+
118+
#### Installation with SQLite
119+
If you are using `pyenv` to manage your Python versions, you can install the SQLite extension with the following command:
120+
```bash
121+
PYTHON_CONFIGURE_OPTS="--enable-loadable-sqlite-extensions" \
122+
LDFLAGS="-L/opt/homebrew/opt/sqlite/lib" \
123+
CPPFLAGS="-I/opt/homebrew/opt/sqlite/include" \
124+
pyenv install 3.10.14
125+
```
126+
And you can the Feast install package via:
127+
```bash
128+
pip install feast[sqlite_vec]
111129
```

docs/reference/online-stores/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,7 @@ Please see [Online Store](../../getting-started/architecture-and-components/onli
6161
{% content-ref url="scylladb.md" %}
6262
[scylladb.md](scylladb.md)
6363
{% endcontent-ref %}
64+
65+
{% content-ref url="remote.md" %}
66+
[remote.md](remote.md)
67+
{% endcontent-ref %}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Remote online store
2+
3+
## Description
4+
5+
This remote online store will let you interact with remote feature server. At this moment this only supports the read operation. You can use this online store and able retrieve online features `store.get_online_features` from remote feature server.
6+
7+
## Examples
8+
9+
The registry is pointing to registry of remote feature store. If it is not accessible then should be configured to use remote registry.
10+
11+
{% code title="feature_store.yaml" %}
12+
```yaml
13+
project: my-local-project
14+
registry: /remote/data/registry.db
15+
provider: local
16+
online_store:
17+
path: http://localhost:6566
18+
type: remote
19+
entity_key_serialization_version: 2
20+
```
21+
{% endcode %}

infra/scripts/pixi/pixi.lock

+419-241
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

infra/scripts/pixi/pixi.toml

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
[project]
22
name = "pixi-feast"
33
channels = ["conda-forge"]
4-
platforms = ["linux-64"]
4+
platforms = ["linux-64", "osx-arm64"]
55

66
[tasks]
77

88
[dependencies]
9-
pip-tools = ">=7.4.1,<7.5"
9+
uv = ">=0.1.39,<0.2"
1010

1111
[feature.py39.dependencies]
1212
python = "~=3.9.0"
1313

1414
[feature.py310.dependencies]
1515
python = "~=3.10.0"
1616

17+
[feature.py311.dependencies]
18+
python = "~=3.11.0"
19+
1720
[environments]
1821
py39 = ["py39"]
19-
py310 = ["py310"]
22+
py310 = ["py310"]
23+
py311 = ["py311"]

sdk/python/feast/feature_store.py

+65-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414
import copy
1515
import itertools
16+
import logging
1617
import os
1718
import warnings
1819
from collections import Counter, defaultdict
@@ -247,6 +248,20 @@ def list_feature_services(self) -> List[FeatureService]:
247248
"""
248249
return self._registry.list_feature_services(self.project)
249250

251+
def list_all_feature_views(
252+
self, allow_cache: bool = False
253+
) -> List[Union[FeatureView, StreamFeatureView, OnDemandFeatureView]]:
254+
"""
255+
Retrieves the list of feature views from the registry.
256+
257+
Args:
258+
allow_cache: Whether to allow returning entities from a cached registry.
259+
260+
Returns:
261+
A list of feature views.
262+
"""
263+
return self._list_all_feature_views(allow_cache)
264+
250265
def list_feature_views(self, allow_cache: bool = False) -> List[FeatureView]:
251266
"""
252267
Retrieves the list of feature views from the registry.
@@ -257,12 +272,50 @@ def list_feature_views(self, allow_cache: bool = False) -> List[FeatureView]:
257272
Returns:
258273
A list of feature views.
259274
"""
275+
logging.warning(
276+
"list_feature_views will make breaking changes. Please use list_batch_feature_views instead. "
277+
"list_feature_views will behave like list_all_feature_views in the future."
278+
)
260279
return self._list_feature_views(allow_cache)
261280

281+
def _list_all_feature_views(
282+
self,
283+
allow_cache: bool = False,
284+
) -> List[Union[FeatureView, StreamFeatureView, OnDemandFeatureView]]:
285+
all_feature_views = (
286+
self._list_feature_views(allow_cache)
287+
+ self._list_stream_feature_views(allow_cache)
288+
+ self.list_on_demand_feature_views(allow_cache)
289+
)
290+
return all_feature_views
291+
262292
def _list_feature_views(
263293
self,
264294
allow_cache: bool = False,
265295
hide_dummy_entity: bool = True,
296+
) -> List[FeatureView]:
297+
logging.warning(
298+
"_list_feature_views will make breaking changes. Please use _list_batch_feature_views instead. "
299+
"_list_feature_views will behave like _list_all_feature_views in the future."
300+
)
301+
feature_views = []
302+
for fv in self._registry.list_feature_views(
303+
self.project, allow_cache=allow_cache
304+
):
305+
if (
306+
hide_dummy_entity
307+
and fv.entities
308+
and fv.entities[0] == DUMMY_ENTITY_NAME
309+
):
310+
fv.entities = []
311+
fv.entity_columns = []
312+
feature_views.append(fv)
313+
return feature_views
314+
315+
def _list_batch_feature_views(
316+
self,
317+
allow_cache: bool = False,
318+
hide_dummy_entity: bool = True,
266319
) -> List[FeatureView]:
267320
feature_views = []
268321
for fv in self._registry.list_feature_views(
@@ -1881,18 +1934,28 @@ def _retrieve_online_documents(
18811934
"Using embedding functionality is not supported for document retrieval. Please embed the query before calling retrieve_online_documents."
18821935
)
18831936
(
1884-
requested_feature_views,
1937+
available_feature_views,
18851938
_,
18861939
) = self._get_feature_views_to_use(
18871940
features=[feature], allow_cache=True, hide_dummy_entity=False
18881941
)
1942+
requested_feature_view_name = (
1943+
feature.split(":")[0] if isinstance(feature, str) else feature
1944+
)
1945+
for feature_view in available_feature_views:
1946+
if feature_view.name == requested_feature_view_name:
1947+
requested_feature_view = feature_view
1948+
if not requested_feature_view:
1949+
raise ValueError(
1950+
f"Feature view {requested_feature_view} not found in the registry."
1951+
)
18891952
requested_feature = (
18901953
feature.split(":")[1] if isinstance(feature, str) else feature
18911954
)
18921955
provider = self._get_provider()
18931956
document_features = self._retrieve_from_online_store(
18941957
provider,
1895-
requested_feature_views[0],
1958+
requested_feature_view,
18961959
requested_feature,
18971960
query,
18981961
top_k,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Copyright 2021 The Feast Authors
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+
import json
15+
import logging
16+
from datetime import datetime
17+
from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Tuple
18+
19+
import requests
20+
from pydantic import StrictStr
21+
22+
from feast import Entity, FeatureView, RepoConfig
23+
from feast.infra.online_stores.online_store import OnlineStore
24+
from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto
25+
from feast.protos.feast.types.Value_pb2 import Value as ValueProto
26+
from feast.repo_config import FeastConfigBaseModel
27+
from feast.type_map import python_values_to_proto_values
28+
from feast.value_type import ValueType
29+
30+
logger = logging.getLogger(__name__)
31+
32+
33+
class RemoteOnlineStoreConfig(FeastConfigBaseModel):
34+
"""Remote Online store config for remote online store"""
35+
36+
type: Literal["remote"] = "remote"
37+
"""Online store type selector"""
38+
39+
path: StrictStr = "http://localhost:6566"
40+
""" str: Path to metadata store.
41+
If type is 'remote', then this is a URL for registry server """
42+
43+
44+
class RemoteOnlineStore(OnlineStore):
45+
"""
46+
remote online store implementation wrapper to communicate with feast online server.
47+
"""
48+
49+
def online_write_batch(
50+
self,
51+
config: RepoConfig,
52+
table: FeatureView,
53+
data: List[
54+
Tuple[EntityKeyProto, Dict[str, ValueProto], datetime, Optional[datetime]]
55+
],
56+
progress: Optional[Callable[[int], Any]],
57+
) -> None:
58+
pass
59+
60+
def online_read(
61+
self,
62+
config: RepoConfig,
63+
table: FeatureView,
64+
entity_keys: List[EntityKeyProto],
65+
requested_features: Optional[List[str]] = None,
66+
) -> List[Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]]:
67+
assert isinstance(config.online_store, RemoteOnlineStoreConfig)
68+
config.online_store.__class__ = RemoteOnlineStoreConfig
69+
70+
req_body = self._construct_online_read_api_json_request(
71+
entity_keys, table, requested_features
72+
)
73+
response = requests.post(
74+
f"{config.online_store.path}/get-online-features", data=req_body
75+
)
76+
if response.status_code == 200:
77+
logger.debug("Able to retrieve the online features from feature server.")
78+
response_json = json.loads(response.text)
79+
event_ts = self._get_event_ts(response_json)
80+
# Iterating over results and converting the API results in column format to row format.
81+
result_tuples: List[
82+
Tuple[Optional[datetime], Optional[Dict[str, ValueProto]]]
83+
] = []
84+
for feature_value_index in range(len(entity_keys)):
85+
feature_values_dict: Dict[str, ValueProto] = dict()
86+
for index, feature_name in enumerate(
87+
response_json["metadata"]["feature_names"]
88+
):
89+
if (
90+
requested_features is not None
91+
and feature_name in requested_features
92+
):
93+
if (
94+
response_json["results"][index]["statuses"][
95+
feature_value_index
96+
]
97+
== "PRESENT"
98+
):
99+
message = python_values_to_proto_values(
100+
[
101+
response_json["results"][index]["values"][
102+
feature_value_index
103+
]
104+
],
105+
ValueType.UNKNOWN,
106+
)
107+
feature_values_dict[feature_name] = message[0]
108+
else:
109+
feature_values_dict[feature_name] = ValueProto()
110+
111+
result_tuples.append((event_ts, feature_values_dict))
112+
return result_tuples
113+
else:
114+
error_msg = f"Unable to retrieve the online store data using feature server API. Error_code={response.status_code}, error_message={response.reason}"
115+
logger.error(error_msg)
116+
raise RuntimeError(error_msg)
117+
118+
def _construct_online_read_api_json_request(
119+
self,
120+
entity_keys: List[EntityKeyProto],
121+
table: FeatureView,
122+
requested_features: Optional[List[str]] = None,
123+
):
124+
api_requested_features = []
125+
if requested_features is not None:
126+
for requested_feature in requested_features:
127+
api_requested_features.append(f"{table.name}:{requested_feature}")
128+
129+
entity_values = []
130+
entity_key = ""
131+
for row in entity_keys:
132+
entity_key = row.join_keys[0]
133+
entity_values.append(
134+
getattr(row.entity_values[0], row.entity_values[0].WhichOneof("val"))
135+
)
136+
137+
req_body = json.dumps(
138+
{
139+
"features": api_requested_features,
140+
"entities": {entity_key: entity_values},
141+
}
142+
)
143+
return req_body
144+
145+
def _check_if_feature_requested(self, feature_name, requested_features):
146+
for requested_feature in requested_features:
147+
if feature_name in requested_feature:
148+
return True
149+
return False
150+
151+
def _get_event_ts(self, response_json) -> datetime:
152+
event_ts = ""
153+
if len(response_json["results"]) > 1:
154+
event_ts = response_json["results"][1]["event_timestamps"][0]
155+
return datetime.fromisoformat(event_ts.replace("Z", "+00:00"))
156+
157+
def update(
158+
self,
159+
config: RepoConfig,
160+
tables_to_delete: Sequence[FeatureView],
161+
tables_to_keep: Sequence[FeatureView],
162+
entities_to_delete: Sequence[Entity],
163+
entities_to_keep: Sequence[Entity],
164+
partial: bool,
165+
):
166+
pass
167+
168+
def teardown(
169+
self,
170+
config: RepoConfig,
171+
tables: Sequence[FeatureView],
172+
entities: Sequence[Entity],
173+
):
174+
pass

0 commit comments

Comments
 (0)