From d2c637d25a33378a08db7e0346fd12b3083aa12b Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 3 Mar 2026 17:05:06 -0500 Subject: [PATCH 01/76] feat: Add MongoDB offline store (ibis-based PIT join, v1 alpha) - MongoDBSource: DataSource backed by a MongoDB collection, schema sampled via \ aggregation (default N=100) - MongoDBOfflineStoreConfig: connection_string + default database - MongoDBOfflineStore: delegates to ibis PIT join engine via in-memory memtable approach - SavedDatasetMongoDBStorage: persist training datasets to MongoDB - _build_data_source_reader/_build_data_source_writer closures capture config (connection_string, database) for MongoDB access Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/__init__.py | 1 + .../contrib/mongodb_offline_store/mongodb.py | 224 ++++++++++++++ .../mongodb_offline_store/mongodb_source.py | 276 ++++++++++++++++++ 3 files changed, 501 insertions(+) create mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py create mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py create mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py @@ -0,0 +1 @@ + diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py new file mode 100644 index 00000000000..482f2cdc88b --- /dev/null +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -0,0 +1,224 @@ +# Copyright 2025 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import warnings +from datetime import datetime +from typing import Any, Callable, List, Optional, Union + +import ibis +import pandas as pd +from ibis.expr.types import Table +from pydantic import StrictStr + +try: + from pymongo import MongoClient +except ImportError: + MongoClient = None # type: ignore[assignment,misc] + +from feast.data_source import DataSource +from feast.errors import ( + FeastExtrasDependencyImportError, + SavedDatasetLocationAlreadyExists, +) +from feast.feature_view import FeatureView +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source import ( + MongoDBSource, +) +from feast.infra.offline_stores.ibis import ( + get_historical_features_ibis, + pull_all_from_table_or_query_ibis, + pull_latest_from_table_or_query_ibis, +) +from feast.infra.offline_stores.offline_store import OfflineStore, RetrievalJob +from feast.infra.registry.base_registry import BaseRegistry +from feast.repo_config import FeastConfigBaseModel, RepoConfig + +# Print RuntimeWarning only once per process. +warnings.simplefilter("once", RuntimeWarning) + + +class MongoDBOfflineStoreConfig(FeastConfigBaseModel): + """Configuration for the MongoDB offline store.""" + + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStore" + """Offline store type selector""" + + connection_string: StrictStr = "mongodb://localhost:27017" + """MongoDB connection URI""" + + database: StrictStr = "feast" + """Default MongoDB database name""" + + +class MongoDBOfflineStore(OfflineStore): + """Offline store backed by MongoDB, using ibis for point-in-time joins.""" + + @staticmethod + def pull_latest_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str], + start_date: datetime, + end_date: datetime, + ) -> RetrievalJob: + assert isinstance(data_source, MongoDBSource) + warnings.warn( + "MongoDB offline store is in alpha. API may change without notice.", + RuntimeWarning, + ) + return pull_latest_from_table_or_query_ibis( + config=config, + data_source=data_source, + join_key_columns=join_key_columns, + feature_name_columns=feature_name_columns, + timestamp_field=timestamp_field, + created_timestamp_column=created_timestamp_column, + start_date=start_date, + end_date=end_date, + data_source_reader=_build_data_source_reader(config), + data_source_writer=_build_data_source_writer(config), # type: ignore[arg-type] + ) + + @staticmethod + def get_historical_features( + config: RepoConfig, + feature_views: List[FeatureView], + feature_refs: List[str], + entity_df: Union[pd.DataFrame, str], + registry: BaseRegistry, + project: str, + full_feature_names: bool = False, + ) -> RetrievalJob: + warnings.warn( + "MongoDB offline store is in alpha. API may change without notice.", + RuntimeWarning, + ) + return get_historical_features_ibis( + config=config, + feature_views=feature_views, + feature_refs=feature_refs, + entity_df=entity_df, + registry=registry, + project=project, + full_feature_names=full_feature_names, + data_source_reader=_build_data_source_reader(config), + data_source_writer=_build_data_source_writer(config), # type: ignore[arg-type] + ) + + @staticmethod + def pull_all_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str] = None, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None, + ) -> RetrievalJob: + assert isinstance(data_source, MongoDBSource) + warnings.warn( + "MongoDB offline store is in alpha. API may change without notice.", + RuntimeWarning, + ) + return pull_all_from_table_or_query_ibis( + config=config, + data_source=data_source, + join_key_columns=join_key_columns, + feature_name_columns=feature_name_columns, + timestamp_field=timestamp_field, + created_timestamp_column=created_timestamp_column, + start_date=start_date, + end_date=end_date, + data_source_reader=_build_data_source_reader(config), + data_source_writer=_build_data_source_writer(config), # type: ignore[arg-type] + ) + + +def _build_data_source_reader(config: RepoConfig) -> Callable[[DataSource, str], Table]: + """Return a closure that fetches a MongoDB collection as an ibis in-memory table.""" + + def reader(data_source: DataSource, repo_path: str) -> Table: + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + assert isinstance(data_source, MongoDBSource) + connection_string = config.offline_store.connection_string + db_name = data_source.database or config.offline_store.database + client: Any = MongoClient(connection_string, tz_aware=True) + try: + docs = list(client[db_name][data_source.collection].find({}, {"_id": 0})) + finally: + client.close() + + df = pd.DataFrame(docs) + if df.empty: + return ibis.memtable(df) + + # Ensure datetime-like columns are timezone-aware UTC pandas timestamps. + for col in df.columns: + if pd.api.types.is_datetime64_any_dtype(df[col]): + if df[col].dt.tz is None: + df[col] = pd.to_datetime(df[col], utc=True) + elif df[col].dtype == object and len(df[col].dropna()) > 0: + sample = df[col].dropna().iloc[0] + if isinstance(sample, datetime): + try: + df[col] = pd.to_datetime(df[col], utc=True) + except Exception: + pass + + return ibis.memtable(df) + + return reader + + +def _build_data_source_writer( + config: RepoConfig, +) -> Callable[[Table, DataSource, str, str, bool], None]: + """Return a closure that writes an ibis table to a MongoDB collection.""" + + def writer( + table: Table, + data_source: DataSource, + repo_path: str, + mode: str = "append", + allow_overwrite: bool = False, + ) -> None: + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + assert isinstance(data_source, MongoDBSource) + connection_string = config.offline_store.connection_string + db_name = data_source.database or config.offline_store.database + location = f"{db_name}.{data_source.collection}" + client: Any = MongoClient(connection_string) + try: + coll = client[db_name][data_source.collection] + if mode == "overwrite": + if not allow_overwrite and coll.estimated_document_count() > 0: + raise SavedDatasetLocationAlreadyExists(location=location) + coll.drop() + records = table.to_pyarrow().to_pylist() + if records: + coll.insert_many(records) + finally: + client.close() + + return writer diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py new file mode 100644 index 00000000000..825f2910f71 --- /dev/null +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py @@ -0,0 +1,276 @@ +# Copyright 2025 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from datetime import datetime +from typing import Any, Callable, Dict, Iterable, Optional, Tuple + +try: + from pymongo import MongoClient +except ImportError: + MongoClient = None # type: ignore[assignment,misc] + +from feast.data_source import DataSource +from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError +from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto +from feast.protos.feast.core.SavedDataset_pb2 import ( + SavedDatasetStorage as SavedDatasetStorageProto, +) +from feast.repo_config import RepoConfig +from feast.saved_dataset import SavedDatasetStorage +from feast.value_type import ValueType + + +def _infer_python_type_str(value: Any) -> Optional[str]: + """Infer a Feast-compatible type string from a Python value returned by pymongo.""" + if value is None: + return None + if isinstance(value, bool): + return "bool" + if isinstance(value, int): + return "int" + if isinstance(value, float): + return "float" + if isinstance(value, str): + return "str" + if isinstance(value, bytes): + return "bytes" + if isinstance(value, datetime): + return "datetime" + if isinstance(value, list): + if not value: + return "list[str]" + elem_type = _infer_python_type_str(value[0]) + if elem_type: + return f"list[{elem_type}]" + return "list[str]" + return None + + +def mongodb_to_feast_value_type(type_str: str) -> ValueType: + """Map a Python type string (from pymongo) to a Feast ValueType.""" + _MAP: Dict[str, ValueType] = { + "str": ValueType.STRING, + "int": ValueType.INT64, + "float": ValueType.DOUBLE, + "bool": ValueType.BOOL, + "bytes": ValueType.BYTES, + "datetime": ValueType.UNIX_TIMESTAMP, + "list[str]": ValueType.STRING_LIST, + "list[int]": ValueType.INT64_LIST, + "list[float]": ValueType.DOUBLE_LIST, + "list[bool]": ValueType.BOOL_LIST, + "list[bytes]": ValueType.BYTES_LIST, + "list[datetime]": ValueType.UNIX_TIMESTAMP_LIST, + } + return _MAP.get(type_str, ValueType.UNKNOWN) + + +class MongoDBOptions: + """Options for a MongoDB data source (database + collection).""" + + def __init__(self, database: str, collection: str): + self._database = database + self._collection = collection + + def to_proto(self) -> DataSourceProto.CustomSourceOptions: + return DataSourceProto.CustomSourceOptions( + configuration=json.dumps( + {"database": self._database, "collection": self._collection} + ).encode() + ) + + @classmethod + def from_proto( + cls, options_proto: DataSourceProto.CustomSourceOptions + ) -> "MongoDBOptions": + config = json.loads(options_proto.configuration.decode("utf8")) + return cls(database=config["database"], collection=config["collection"]) + + +class MongoDBSource(DataSource): + """A MongoDB collection as a Feast offline data source.""" + + def source_type(self) -> DataSourceProto.SourceType.ValueType: + return DataSourceProto.CUSTOM_SOURCE + + def __init__( + self, + name: Optional[str] = None, + database: Optional[str] = None, + collection: Optional[str] = None, + timestamp_field: Optional[str] = "", + created_timestamp_column: Optional[str] = "", + field_mapping: Optional[Dict[str, str]] = None, + description: Optional[str] = "", + tags: Optional[Dict[str, str]] = None, + owner: Optional[str] = "", + schema_sample_size: int = 100, + ): + if name is None and collection is None: + raise DataSourceNoNameException() + name = name or collection + assert name + + self._mongodb_options = MongoDBOptions( + database=database or "", + collection=collection or name, + ) + self._schema_sample_size = schema_sample_size + + super().__init__( + name=name, + timestamp_field=timestamp_field, + created_timestamp_column=created_timestamp_column, + field_mapping=field_mapping, + description=description, + tags=tags, + owner=owner, + ) + + def __hash__(self): + return super().__hash__() + + def __eq__(self, other): + if not isinstance(other, MongoDBSource): + raise TypeError( + "Comparisons should only involve MongoDBSource class objects." + ) + return ( + super().__eq__(other) + and self._mongodb_options._database == other._mongodb_options._database + and self._mongodb_options._collection == other._mongodb_options._collection + and self.timestamp_field == other.timestamp_field + and self.created_timestamp_column == other.created_timestamp_column + and self.field_mapping == other.field_mapping + ) + + @property + def database(self) -> str: + return self._mongodb_options._database + + @property + def collection(self) -> str: + return self._mongodb_options._collection + + @staticmethod + def from_proto(data_source: DataSourceProto) -> "MongoDBSource": + assert data_source.HasField("custom_options") + options = json.loads(data_source.custom_options.configuration) + return MongoDBSource( + name=data_source.name, + database=options["database"], + collection=options["collection"], + field_mapping=dict(data_source.field_mapping), + timestamp_field=data_source.timestamp_field, + created_timestamp_column=data_source.created_timestamp_column, + description=data_source.description, + tags=dict(data_source.tags), + owner=data_source.owner, + ) + + def _to_proto_impl(self) -> DataSourceProto: + data_source_proto = DataSourceProto( + name=self.name, + type=DataSourceProto.CUSTOM_SOURCE, + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source.MongoDBSource", + field_mapping=self.field_mapping, + custom_options=self._mongodb_options.to_proto(), + description=self.description, + tags=self.tags, + owner=self.owner, + ) + data_source_proto.timestamp_field = self.timestamp_field + data_source_proto.created_timestamp_column = self.created_timestamp_column + return data_source_proto + + def validate(self, config: RepoConfig): + pass + + @staticmethod + def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: + return mongodb_to_feast_value_type + + def get_table_query_string(self) -> str: + return f"{self._mongodb_options._database}.{self._mongodb_options._collection}" + + def get_table_column_names_and_types( + self, config: RepoConfig + ) -> Iterable[Tuple[str, str]]: + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + connection_string = config.offline_store.connection_string + db_name = self.database or config.offline_store.database + client: Any = MongoClient(connection_string, tz_aware=True) + try: + docs = list( + client[db_name][self.collection].aggregate( + [{"$sample": {"size": self._schema_sample_size}}] + ) + ) + finally: + client.close() + + field_type_counts: Dict[str, Dict[str, int]] = {} + for doc in docs: + for field, value in doc.items(): + if field == "_id": + continue + type_str = _infer_python_type_str(value) + if type_str is None: + continue + field_type_counts.setdefault(field, {}) + field_type_counts[field][type_str] = ( + field_type_counts[field].get(type_str, 0) + 1 + ) + + return [ + (field, max(counts, key=lambda t: counts[t])) + for field, counts in field_type_counts.items() + ] + + +class SavedDatasetMongoDBStorage(SavedDatasetStorage): + """Persists a Feast SavedDataset into a MongoDB collection.""" + + _proto_attr_name = "custom_storage" + + mongodb_options: MongoDBOptions + + def __init__(self, database: str, collection: str): + self.mongodb_options = MongoDBOptions( + database=database, + collection=collection, + ) + + @staticmethod + def from_proto( + storage_proto: SavedDatasetStorageProto, + ) -> "SavedDatasetMongoDBStorage": + options = json.loads(storage_proto.custom_storage.configuration) + return SavedDatasetMongoDBStorage( + database=options["database"], + collection=options["collection"], + ) + + def to_proto(self) -> SavedDatasetStorageProto: + return SavedDatasetStorageProto(custom_storage=self.mongodb_options.to_proto()) + + def to_data_source(self) -> DataSource: + return MongoDBSource( + database=self.mongodb_options._database, + collection=self.mongodb_options._collection, + ) From 77f243ea326cb5e563a86d3741914e7e0150b18f Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 4 Mar 2026 12:35:19 -0500 Subject: [PATCH 02/76] refactor: improve MongoDB offline store code quality - Update copyright headers to 2026 - Move mongodb_to_feast_value_type to feast/type_map.py, consistent with pg_type_to_feast_value_type and cb_columnar_type_to_feast_value_type - Add docstrings to MongoDBOptions.to_proto/from_proto, MongoDBSource class, and get_table_column_names_and_types - Replace dead 'assert name' with cast(str, ...) for type-checker safety - Add explanatory comment to validate() stub - Remove module-level warnings.simplefilter('once', RuntimeWarning), which was a process-wide side effect; per-call warnings.warn is enough - Convert all assert isinstance(data_source, MongoDBSource) guards to ValueError with descriptive messages in both public API methods and the reader/writer closures - Fix bug: add tz_aware=True to MongoClient in the writer closure, matching the reader, to ensure consistent timezone-aware datetime handling across read and write paths Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 31 ++++++++--- .../mongodb_offline_store/mongodb_source.py | 55 +++++++++++-------- sdk/python/feast/type_map.py | 24 ++++++++ 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 482f2cdc88b..23b1295286e 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -1,4 +1,4 @@ -# Copyright 2025 The Feast Authors +# Copyright 2026 The Feast Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -44,9 +44,6 @@ from feast.infra.registry.base_registry import BaseRegistry from feast.repo_config import FeastConfigBaseModel, RepoConfig -# Print RuntimeWarning only once per process. -warnings.simplefilter("once", RuntimeWarning) - class MongoDBOfflineStoreConfig(FeastConfigBaseModel): """Configuration for the MongoDB offline store.""" @@ -75,7 +72,11 @@ def pull_latest_from_table_or_query( start_date: datetime, end_date: datetime, ) -> RetrievalJob: - assert isinstance(data_source, MongoDBSource) + if not isinstance(data_source, MongoDBSource): + raise ValueError( + f"MongoDBOfflineStore expected a MongoDBSource, " + f"got {type(data_source).__name__!r}." + ) warnings.warn( "MongoDB offline store is in alpha. API may change without notice.", RuntimeWarning, @@ -130,7 +131,11 @@ def pull_all_from_table_or_query( start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, ) -> RetrievalJob: - assert isinstance(data_source, MongoDBSource) + if not isinstance(data_source, MongoDBSource): + raise ValueError( + f"MongoDBOfflineStore expected a MongoDBSource, " + f"got {type(data_source).__name__!r}." + ) warnings.warn( "MongoDB offline store is in alpha. API may change without notice.", RuntimeWarning, @@ -157,7 +162,11 @@ def reader(data_source: DataSource, repo_path: str) -> Table: raise FeastExtrasDependencyImportError( "mongodb", "pymongo is not installed." ) - assert isinstance(data_source, MongoDBSource) + if not isinstance(data_source, MongoDBSource): + raise ValueError( + f"MongoDBOfflineStore reader expected a MongoDBSource, " + f"got {type(data_source).__name__!r}." + ) connection_string = config.offline_store.connection_string db_name = data_source.database or config.offline_store.database client: Any = MongoClient(connection_string, tz_aware=True) @@ -204,11 +213,15 @@ def writer( raise FeastExtrasDependencyImportError( "mongodb", "pymongo is not installed." ) - assert isinstance(data_source, MongoDBSource) + if not isinstance(data_source, MongoDBSource): + raise ValueError( + f"MongoDBOfflineStore writer expected a MongoDBSource, " + f"got {type(data_source).__name__!r}." + ) connection_string = config.offline_store.connection_string db_name = data_source.database or config.offline_store.database location = f"{db_name}.{data_source.collection}" - client: Any = MongoClient(connection_string) + client: Any = MongoClient(connection_string, tz_aware=True) try: coll = client[db_name][data_source.collection] if mode == "overwrite": diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py index 825f2910f71..ee55fe24e6a 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py @@ -1,4 +1,4 @@ -# Copyright 2025 The Feast Authors +# Copyright 2026 The Feast Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ import json from datetime import datetime -from typing import Any, Callable, Dict, Iterable, Optional, Tuple +from typing import Any, Callable, Dict, Iterable, Optional, Tuple, cast try: from pymongo import MongoClient @@ -29,6 +29,7 @@ ) from feast.repo_config import RepoConfig from feast.saved_dataset import SavedDatasetStorage +from feast.type_map import mongodb_to_feast_value_type from feast.value_type import ValueType @@ -58,25 +59,6 @@ def _infer_python_type_str(value: Any) -> Optional[str]: return None -def mongodb_to_feast_value_type(type_str: str) -> ValueType: - """Map a Python type string (from pymongo) to a Feast ValueType.""" - _MAP: Dict[str, ValueType] = { - "str": ValueType.STRING, - "int": ValueType.INT64, - "float": ValueType.DOUBLE, - "bool": ValueType.BOOL, - "bytes": ValueType.BYTES, - "datetime": ValueType.UNIX_TIMESTAMP, - "list[str]": ValueType.STRING_LIST, - "list[int]": ValueType.INT64_LIST, - "list[float]": ValueType.DOUBLE_LIST, - "list[bool]": ValueType.BOOL_LIST, - "list[bytes]": ValueType.BYTES_LIST, - "list[datetime]": ValueType.UNIX_TIMESTAMP_LIST, - } - return _MAP.get(type_str, ValueType.UNKNOWN) - - class MongoDBOptions: """Options for a MongoDB data source (database + collection).""" @@ -85,6 +67,7 @@ def __init__(self, database: str, collection: str): self._collection = collection def to_proto(self) -> DataSourceProto.CustomSourceOptions: + """Serialize database and collection names as JSON into a CustomSourceOptions proto.""" return DataSourceProto.CustomSourceOptions( configuration=json.dumps( {"database": self._database, "collection": self._collection} @@ -95,12 +78,28 @@ def to_proto(self) -> DataSourceProto.CustomSourceOptions: def from_proto( cls, options_proto: DataSourceProto.CustomSourceOptions ) -> "MongoDBOptions": + """Deserialize a CustomSourceOptions proto back into a MongoDBOptions instance.""" config = json.loads(options_proto.configuration.decode("utf8")) return cls(database=config["database"], collection=config["collection"]) class MongoDBSource(DataSource): - """A MongoDB collection as a Feast offline data source.""" + """A MongoDB collection used as a Feast offline data source. + + ``name`` is the logical Feast name for this source. If omitted, it defaults + to the value of ``collection``. At least one of ``name`` or ``collection`` + must be supplied. + + ``database`` is the MongoDB database that contains the collection. When + omitted it falls back to ``MongoDBOfflineStoreConfig.database`` at query + time, so a single store-level default can be shared across many sources. + + ``schema_sample_size`` controls how many documents are randomly sampled + when Feast infers the collection schema (used by ``feast apply`` and + ``get_table_column_names_and_types``). Increase it for collections with + highly variable document shapes; decrease it to speed up ``feast apply`` + at the cost of schema coverage. + """ def source_type(self) -> DataSourceProto.SourceType.ValueType: return DataSourceProto.CUSTOM_SOURCE @@ -120,8 +119,8 @@ def __init__( ): if name is None and collection is None: raise DataSourceNoNameException() - name = name or collection - assert name + # At least one of name / collection is non-None; cast to satisfy the type checker. + name = cast(str, name or collection) self._mongodb_options = MongoDBOptions( database=database or "", @@ -196,6 +195,8 @@ def _to_proto_impl(self) -> DataSourceProto: return data_source_proto def validate(self, config: RepoConfig): + # No upfront schema validation is required for MongoDB; the connection + # is exercised lazily when features are actually retrieved. pass @staticmethod @@ -208,6 +209,12 @@ def get_table_query_string(self) -> str: def get_table_column_names_and_types( self, config: RepoConfig ) -> Iterable[Tuple[str, str]]: + """Sample documents from the collection to infer field names and their Feast type strings. + + Uses ``$sample`` to fetch up to ``schema_sample_size`` documents, then + picks the most-frequent Python type observed per field. The ``_id`` + field is always excluded. + """ if MongoClient is None: raise FeastExtrasDependencyImportError( "mongodb", "pymongo is not installed." diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 5e77f532c9c..b383963c0c9 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -1762,6 +1762,30 @@ def cb_columnar_type_to_feast_value_type(type_str: str) -> ValueType: return value +def mongodb_to_feast_value_type(type_str: str) -> ValueType: + """Map a Python type string (as inferred from pymongo documents) to a Feast ValueType. + + The type strings are produced by + ``feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source._infer_python_type_str``. + Unrecognised strings are mapped to ``ValueType.UNKNOWN``. + """ + type_map: Dict[str, ValueType] = { + "str": ValueType.STRING, + "int": ValueType.INT64, + "float": ValueType.DOUBLE, + "bool": ValueType.BOOL, + "bytes": ValueType.BYTES, + "datetime": ValueType.UNIX_TIMESTAMP, + "list[str]": ValueType.STRING_LIST, + "list[int]": ValueType.INT64_LIST, + "list[float]": ValueType.DOUBLE_LIST, + "list[bool]": ValueType.BOOL_LIST, + "list[bytes]": ValueType.BYTES_LIST, + "list[datetime]": ValueType.UNIX_TIMESTAMP_LIST, + } + return type_map.get(type_str, ValueType.UNKNOWN) + + def convert_scalar_column( series: pd.Series, value_type: ValueType, target_pandas_type: str ) -> pd.Series: From bb134a2bdf199efc3818249ff4e05e1b6910798f Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Mon, 9 Mar 2026 10:13:47 -0400 Subject: [PATCH 03/76] Started work on full Mongo/MQL implementation. Kept MongoDBOfflineStoreIbis and MongoDBOfflineStoreNative Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 421 +++++++++++++++++- 1 file changed, 413 insertions(+), 8 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 23b1295286e..89794e3ba8d 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -13,11 +13,12 @@ # limitations under the License. import warnings -from datetime import datetime -from typing import Any, Callable, List, Optional, Union +from datetime import datetime, timezone +from typing import Any, Callable, Dict, List, Optional, Union import ibis import pandas as pd +import pyarrow from ibis.expr.types import Table from pydantic import StrictStr @@ -40,15 +41,23 @@ pull_all_from_table_or_query_ibis, pull_latest_from_table_or_query_ibis, ) -from feast.infra.offline_stores.offline_store import OfflineStore, RetrievalJob +from feast.infra.offline_stores.offline_store import ( + OfflineStore, + RetrievalJob, + RetrievalMetadata, +) +from feast.infra.offline_stores.offline_utils import ( + infer_event_timestamp_from_entity_df, +) from feast.infra.registry.base_registry import BaseRegistry from feast.repo_config import FeastConfigBaseModel, RepoConfig +from feast.saved_dataset import SavedDatasetStorage -class MongoDBOfflineStoreConfig(FeastConfigBaseModel): - """Configuration for the MongoDB offline store.""" +class MongoDBOfflineStoreIbisConfig(FeastConfigBaseModel): + """Configuration for the MongoDB Ibis-backed offline store.""" - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStore" + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStoreIbis" """Offline store type selector""" connection_string: StrictStr = "mongodb://localhost:27017" @@ -58,8 +67,8 @@ class MongoDBOfflineStoreConfig(FeastConfigBaseModel): """Default MongoDB database name""" -class MongoDBOfflineStore(OfflineStore): - """Offline store backed by MongoDB, using ibis for point-in-time joins.""" +class MongoDBOfflineStoreIbis(OfflineStore): + """Offline store backed by MongoDB, using Ibis for point-in-time joins.""" @staticmethod def pull_latest_from_table_or_query( @@ -235,3 +244,399 @@ def writer( client.close() return writer + + +# --------------------------------------------------------------------------- +# Native MQL implementation +# --------------------------------------------------------------------------- + + +class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): + """Configuration for the MongoDB native-MQL offline store.""" + + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStoreNative" + """Offline store type selector""" + + connection_string: StrictStr = "mongodb://localhost:27017" + """MongoDB connection URI""" + + database: StrictStr = "feast" + """Default MongoDB database name""" + + +def _fetch_collection_as_arrow( + connection_string: str, + db_name: str, + collection: str, + pipeline: Optional[List[Dict]] = None, +) -> pyarrow.Table: + """Run an aggregation pipeline (or full scan) via PyMongo and return a pyarrow Table. + + If *pipeline* is None the entire collection is scanned (``_id`` excluded). + The ``_id`` field is stripped from every result document before conversion. + """ + if MongoClient is None: + raise FeastExtrasDependencyImportError("mongodb", "pymongo is not installed.") + client: Any = MongoClient(connection_string, tz_aware=True) + try: + if pipeline is not None: + docs = list(client[db_name][collection].aggregate(pipeline)) + else: + docs = list(client[db_name][collection].find({}, {"_id": 0})) + finally: + client.close() + + if not docs: + return pyarrow.table({}) + + for doc in docs: + doc.pop("_id", None) + + return pyarrow.Table.from_pylist(docs) + + +class MongoDBNativeRetrievalJob(RetrievalJob): + """A RetrievalJob whose results come from a lazy PyMongo query callable. + + The callable is only executed when the caller materialises the job (e.g. + ``to_df()``, ``to_arrow()``, ``persist()``). + """ + + def __init__( + self, + query_fn: Callable[[], pyarrow.Table], + full_feature_names: bool, + on_demand_feature_views: List, + metadata: Optional[RetrievalMetadata], + config: RepoConfig, + ) -> None: + super().__init__() + self._query_fn = query_fn + self._full_feature_names = full_feature_names + self._on_demand_feature_views = on_demand_feature_views or [] + self._metadata = metadata + self._config = config + + def _to_arrow_internal(self, timeout: Optional[int] = None) -> pyarrow.Table: + return self._query_fn() + + def _to_df_internal(self, timeout: Optional[int] = None) -> pd.DataFrame: + return self._to_arrow_internal().to_pandas() + + @property + def full_feature_names(self) -> bool: + return self._full_feature_names + + @property + def on_demand_feature_views(self) -> List: + return self._on_demand_feature_views + + @property + def metadata(self) -> Optional[RetrievalMetadata]: + return self._metadata + + def persist( + self, + storage: SavedDatasetStorage, + allow_overwrite: bool = False, + timeout: Optional[int] = None, + ) -> None: + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + data_source = storage.to_data_source() + if not isinstance(data_source, MongoDBSource): + raise ValueError( + f"MongoDBNativeRetrievalJob.persist expected a MongoDBSource storage, " + f"got {type(data_source).__name__!r}." + ) + table = self._to_arrow_internal() + connection_string = self._config.offline_store.connection_string + db_name = data_source.database or self._config.offline_store.database + location = f"{db_name}.{data_source.collection}" + client: Any = MongoClient(connection_string, tz_aware=True) + try: + coll = client[db_name][data_source.collection] + if not allow_overwrite and coll.estimated_document_count() > 0: + raise SavedDatasetLocationAlreadyExists(location=location) + coll.drop() + records = table.to_pylist() + if records: + coll.insert_many(records) + finally: + client.close() + + +class MongoDBOfflineStoreNative(OfflineStore): + """Offline store backed by MongoDB using native MQL aggregation pipelines. + + Compared with :class:`MongoDBOfflineStoreIbis`, this implementation avoids + the Ibis dependency entirely. The three main workflows map to: + + * ``offline_write_batch`` – Arrow → ``insert_many`` + * ``pull_latest_from_table_or_query`` – ``$match`` → ``$sort`` → ``$group`` + * ``pull_all_from_table_or_query`` – ``$match`` → ``$project`` + * ``get_historical_features`` – per-collection fetch + ``merge_asof`` + """ + + @staticmethod + def offline_write_batch( + config: RepoConfig, + feature_view: FeatureView, + table: pyarrow.Table, + progress: Optional[Callable[[int], Any]], + ) -> None: + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + data_source = feature_view.batch_source + if not isinstance(data_source, MongoDBSource): + raise ValueError( + f"MongoDBOfflineStoreNative.offline_write_batch expected a MongoDBSource, " + f"got {type(data_source).__name__!r}." + ) + connection_string = config.offline_store.connection_string + db_name = data_source.database or config.offline_store.database + records = table.to_pylist() + client: Any = MongoClient(connection_string, tz_aware=True) + try: + coll = client[db_name][data_source.collection] + if records: + coll.insert_many(records) + if progress: + progress(len(records)) + finally: + client.close() + + @staticmethod + def pull_latest_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str], + start_date: datetime, + end_date: datetime, + ) -> RetrievalJob: + if not isinstance(data_source, MongoDBSource): + raise ValueError( + f"MongoDBOfflineStoreNative expected a MongoDBSource, " + f"got {type(data_source).__name__!r}." + ) + warnings.warn( + "MongoDB offline store (native) is in alpha. API may change without notice.", + RuntimeWarning, + ) + start_utc = start_date.astimezone(tz=timezone.utc) + end_utc = end_date.astimezone(tz=timezone.utc) + connection_string = config.offline_store.connection_string + db_name = data_source.database or config.offline_store.database + collection = data_source.collection + + sort_spec: Dict = {timestamp_field: -1} + if created_timestamp_column: + sort_spec[created_timestamp_column] = -1 + + group_id = {k: f"${k}" for k in join_key_columns} + group_stage: Dict = { + "_id": group_id, # todo this isn't correct. or i don't follow + **{f: {"$first": f"${f}"} for f in feature_name_columns}, + timestamp_field: {"$first": f"${timestamp_field}"}, + } + if created_timestamp_column: + group_stage[created_timestamp_column] = { + "$first": f"${created_timestamp_column}" + } + + project_stage: Dict = { + "_id": 0, + **{k: f"$_id.{k}" for k in join_key_columns}, # todo here too + **{f: 1 for f in feature_name_columns}, + timestamp_field: 1, + } + if created_timestamp_column: + project_stage[created_timestamp_column] = 1 + + pipeline = [ + {"$match": {timestamp_field: {"$gte": start_utc, "$lte": end_utc}}}, + {"$sort": sort_spec}, + {"$group": group_stage}, + {"$project": project_stage}, + ] + + def _run() -> pyarrow.Table: + return _fetch_collection_as_arrow( + connection_string, db_name, collection, pipeline + ) + + return MongoDBNativeRetrievalJob( + query_fn=_run, + full_feature_names=False, + on_demand_feature_views=[], + metadata=None, + config=config, + ) + + @staticmethod + def pull_all_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str] = None, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None, + ) -> RetrievalJob: + if not isinstance(data_source, MongoDBSource): + raise ValueError( + f"MongoDBOfflineStoreNative expected a MongoDBSource, " + f"got {type(data_source).__name__!r}." + ) + warnings.warn( + "MongoDB offline store (native) is in alpha. API may change without notice.", + RuntimeWarning, + ) + connection_string = config.offline_store.connection_string + db_name = data_source.database or config.offline_store.database + collection = data_source.collection + + fields = join_key_columns + feature_name_columns + [timestamp_field] + if created_timestamp_column: + fields.append(created_timestamp_column) + + match_filter: Dict = {} + if start_date or end_date: + ts_filter: Dict = {} + if start_date: + ts_filter["$gte"] = start_date.astimezone(tz=timezone.utc) + if end_date: + ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) + match_filter[timestamp_field] = ts_filter + + pipeline = [ + {"$match": match_filter}, + {"$project": {"_id": 0, **{f: 1 for f in fields}}}, + ] + + def _run() -> pyarrow.Table: + return _fetch_collection_as_arrow( + connection_string, db_name, collection, pipeline + ) + + return MongoDBNativeRetrievalJob( + query_fn=_run, + full_feature_names=False, + on_demand_feature_views=[], + metadata=None, + config=config, + ) + + @staticmethod + def get_historical_features( + config: RepoConfig, + feature_views: List[FeatureView], + feature_refs: List[str], + entity_df: Union[pd.DataFrame, str], + registry: BaseRegistry, + project: str, + full_feature_names: bool = False, + ) -> RetrievalJob: + if isinstance(entity_df, str): + raise ValueError( + "MongoDBOfflineStoreNative does not support SQL entity_df strings. " + "Pass a pandas DataFrame instead." + ) + warnings.warn( + "MongoDB offline store (native) is in alpha. API may change without notice.", # todo change wording: alpha -> preview + RuntimeWarning, + ) + connection_string = config.offline_store.connection_string + default_db = config.offline_store.database + + entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) + event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) + + # Map "feature_view:feature" refs → {fv_name: [feature, ...]} + fv_to_features: Dict[str, List[str]] = {} + for ref in feature_refs: + fv_name, feat_name = ref.split(":", 1) + fv_to_features.setdefault(fv_name, []).append(feat_name) + + fv_by_name = {fv.name: fv for fv in feature_views} + + def _run() -> pyarrow.Table: + result = entity_df.copy() + # Ensure the entity timestamp is tz-aware UTC for merge_asof + if result[event_timestamp_col].dt.tz is None: + result[event_timestamp_col] = pd.to_datetime( + result[event_timestamp_col], utc=True + ) + result = result.sort_values(event_timestamp_col) + + for fv_name, features in fv_to_features.items(): + fv = fv_by_name[fv_name] + source = fv.batch_source + if not isinstance(source, MongoDBSource): + raise ValueError( + f"MongoDBOfflineStoreNative: feature view {fv_name!r} has " + f"a non-MongoDBSource batch source ({type(source).__name__!r})." + ) + db_name = source.database or default_db + ts_field = source.timestamp_field + join_keys = [e.name for e in fv.entity_columns] + + arrow_table = _fetch_collection_as_arrow( + connection_string, db_name, source.collection + ) + if arrow_table.num_rows == 0: + for f in features: + col = f"{fv_name}__{f}" if full_feature_names else f + result[col] = None + continue + + feature_df = arrow_table.to_pandas() + # Ensure tz-aware UTC + if feature_df[ts_field].dt.tz is None: + feature_df[ts_field] = pd.to_datetime( + feature_df[ts_field], utc=True + ) + feature_df = feature_df.sort_values(ts_field) + + col_rename = { + f: (f"{fv_name}__{f}" if full_feature_names else f) + for f in features + } + cols_to_select = join_keys + features + [ts_field] + feature_df = feature_df[cols_to_select].rename(columns=col_rename) + out_features = list(col_rename.values()) + + merged = pd.merge_asof( + result, + feature_df, + left_on=event_timestamp_col, + right_on=ts_field, + by=join_keys, + direction="backward", + ) + # Apply TTL: null out features whose timestamp is too far in the past + if fv.ttl: + cutoff = merged[event_timestamp_col] - fv.ttl + too_old = merged[ts_field] < cutoff + for col in out_features: + merged.loc[too_old, col] = None + + result = merged.drop(columns=[ts_field], errors="ignore") + + return pyarrow.Table.from_pandas(result, preserve_index=False) + + return MongoDBNativeRetrievalJob( + query_fn=_run, + full_feature_names=full_feature_names, + on_demand_feature_views=[], + metadata=None, + config=config, + ) From 46e67b7de4ea8b71cb298f087032f597afae1de3 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 17 Mar 2026 11:14:25 -0400 Subject: [PATCH 04/76] refactor: rename alpha to preview, clarify MQL pipeline comments Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 89794e3ba8d..ee37b11c418 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -87,7 +87,7 @@ def pull_latest_from_table_or_query( f"got {type(data_source).__name__!r}." ) warnings.warn( - "MongoDB offline store is in alpha. API may change without notice.", + "MongoDB offline store is in preview. API may change without notice.", RuntimeWarning, ) return pull_latest_from_table_or_query_ibis( @@ -114,7 +114,7 @@ def get_historical_features( full_feature_names: bool = False, ) -> RetrievalJob: warnings.warn( - "MongoDB offline store is in alpha. API may change without notice.", + "MongoDB offline store is in preview. API may change without notice.", RuntimeWarning, ) return get_historical_features_ibis( @@ -146,7 +146,7 @@ def pull_all_from_table_or_query( f"got {type(data_source).__name__!r}." ) warnings.warn( - "MongoDB offline store is in alpha. API may change without notice.", + "MongoDB offline store is in preview. API may change without notice.", RuntimeWarning, ) return pull_all_from_table_or_query_ibis( @@ -178,7 +178,7 @@ def reader(data_source: DataSource, repo_path: str) -> Table: ) connection_string = config.offline_store.connection_string db_name = data_source.database or config.offline_store.database - client: Any = MongoClient(connection_string, tz_aware=True) + client: Any = MongoClient(connection_string) try: docs = list(client[db_name][data_source.collection].find({}, {"_id": 0})) finally: @@ -188,17 +188,17 @@ def reader(data_source: DataSource, repo_path: str) -> Table: if df.empty: return ibis.memtable(df) - # Ensure datetime-like columns are timezone-aware UTC pandas timestamps. + # Localize naive datetime columns to UTC. MongoDB stores all dates as UTC, + # and with tz_aware=False (default), pymongo returns naive datetime objects. + # We convert them to timezone-aware UTC timestamps for pyarrow compatibility. for col in df.columns: - if pd.api.types.is_datetime64_any_dtype(df[col]): - if df[col].dt.tz is None: - df[col] = pd.to_datetime(df[col], utc=True) - elif df[col].dtype == object and len(df[col].dropna()) > 0: + if df[col].dtype == object and len(df[col].dropna()) > 0: sample = df[col].dropna().iloc[0] if isinstance(sample, datetime): try: df[col] = pd.to_datetime(df[col], utc=True) - except Exception: + except (ValueError, TypeError): + # Skip columns that can't be converted (e.g., mixed types) pass return ibis.memtable(df) @@ -427,7 +427,7 @@ def pull_latest_from_table_or_query( f"got {type(data_source).__name__!r}." ) warnings.warn( - "MongoDB offline store (native) is in alpha. API may change without notice.", + "MongoDB offline store (native) is in preview. API may change without notice.", RuntimeWarning, ) start_utc = start_date.astimezone(tz=timezone.utc) @@ -436,13 +436,17 @@ def pull_latest_from_table_or_query( db_name = data_source.database or config.offline_store.database collection = data_source.collection + # Sort by timestamp descending so $first in $group gets the latest document sort_spec: Dict = {timestamp_field: -1} if created_timestamp_column: sort_spec[created_timestamp_column] = -1 + # Group by entity/join keys. _id becomes a subdocument like {driver_id: 1}. + # $first grabs values from the first document in each group (the latest, + # due to prior $sort). group_id = {k: f"${k}" for k in join_key_columns} group_stage: Dict = { - "_id": group_id, # todo this isn't correct. or i don't follow + "_id": group_id, **{f: {"$first": f"${f}"} for f in feature_name_columns}, timestamp_field: {"$first": f"${timestamp_field}"}, } @@ -451,9 +455,11 @@ def pull_latest_from_table_or_query( "$first": f"${created_timestamp_column}" } + # Project to flatten the output: extract join keys from _id subdocument, + # include feature columns directly. Excludes the _id field from output. project_stage: Dict = { "_id": 0, - **{k: f"$_id.{k}" for k in join_key_columns}, # todo here too + **{k: f"$_id.{k}" for k in join_key_columns}, **{f: 1 for f in feature_name_columns}, timestamp_field: 1, } @@ -497,7 +503,7 @@ def pull_all_from_table_or_query( f"got {type(data_source).__name__!r}." ) warnings.warn( - "MongoDB offline store (native) is in alpha. API may change without notice.", + "MongoDB offline store (native) is in preview. API may change without notice.", RuntimeWarning, ) connection_string = config.offline_store.connection_string @@ -551,7 +557,7 @@ def get_historical_features( "Pass a pandas DataFrame instead." ) warnings.warn( - "MongoDB offline store (native) is in alpha. API may change without notice.", # todo change wording: alpha -> preview + "MongoDB offline store (native) is in preview. API may change without notice.", RuntimeWarning, ) connection_string = config.offline_store.connection_string From d0c00caae6a74e4264271431c7f5560e34280119 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 17 Mar 2026 11:27:44 -0400 Subject: [PATCH 05/76] Added unit tests for offline store retrieval, requiring docker and pymongo, skipping as natural. Signed-off-by: Casey Clements --- .../contrib/test_mongodb_offline_retrieval.py | 388 ++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py new file mode 100644 index 00000000000..cd83e33c0d3 --- /dev/null +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py @@ -0,0 +1,388 @@ +""" +Unit tests for MongoDB offline store (Ibis-based implementation). + +Docker-dependent tests are marked with ``@_requires_docker`` and are skipped when +Docker is unavailable. +""" + +from datetime import datetime, timedelta +from typing import Generator +from unittest.mock import MagicMock + +import pandas as pd +import pytest +import pytz + +pytest.importorskip("pymongo") + +from pymongo import MongoClient +from testcontainers.mongodb import MongoDbContainer + +from feast import Entity, FeatureView, Field +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb import ( + MongoDBOfflineStoreIbis, + MongoDBOfflineStoreIbisConfig, +) +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source import ( + MongoDBSource, +) +from feast.repo_config import RepoConfig +from feast.types import Float64, Int64 +from feast.value_type import ValueType + +# Check if Docker is available +docker_available = False +try: + import docker + + try: + client = docker.from_env() + client.ping() + docker_available = True + except Exception: + pass +except ImportError: + pass + +_requires_docker = pytest.mark.skipif( + not docker_available, + reason="Docker is not available or not running.", +) + + +@pytest.fixture(scope="module") +def mongodb_container() -> Generator[MongoDbContainer, None, None]: + """Start a MongoDB container for testing.""" + container = MongoDbContainer( + "mongo:latest", + username="test", + password="test", # pragma: allowlist secret + ).with_exposed_ports(27017) + container.start() + yield container + container.stop() + + +@pytest.fixture +def mongodb_connection_string(mongodb_container: MongoDbContainer) -> str: + """Get MongoDB connection string from the container.""" + exposed_port = mongodb_container.get_exposed_port(27017) + return f"mongodb://test:test@localhost:{exposed_port}" # pragma: allowlist secret + + +@pytest.fixture +def repo_config(mongodb_connection_string: str) -> RepoConfig: + """Create a RepoConfig with MongoDB offline store.""" + return RepoConfig( + project="test_project", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreIbisConfig( + connection_string=mongodb_connection_string, + database="feast_test", + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=3, + ) + + +@pytest.fixture +def sample_data(mongodb_connection_string: str) -> datetime: + """Insert sample driver stats data into MongoDB. + + Returns the 'now' timestamp used as the latest event_timestamp. + + Note: The collection name 'driver_stats' is defined in the MongoDBSource + (see driver_source fixture), not in the RepoConfig. RepoConfig provides + connection_string and database; the source defines the collection. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["driver_stats"] + collection.drop() + + now = datetime.now(tz=pytz.UTC) + docs = [ + { + "driver_id": 1, + "conv_rate": 0.5, + "acc_rate": 0.9, + "event_timestamp": now - timedelta(hours=2), + }, + { + "driver_id": 1, + "conv_rate": 0.6, + "acc_rate": 0.85, + "event_timestamp": now - timedelta(hours=1), + }, + {"driver_id": 1, "conv_rate": 0.7, "acc_rate": 0.8, "event_timestamp": now}, + { + "driver_id": 2, + "conv_rate": 0.3, + "acc_rate": 0.95, + "event_timestamp": now - timedelta(hours=2), + }, + # Driver 2 has no "now" timestamp - only data from 2 hours ago + # This tests that pull_latest correctly handles entities with different latest timestamps + ] + collection.insert_many(docs) + client.close() + return now + + +@pytest.fixture +def driver_source() -> MongoDBSource: + """Create a MongoDBSource for driver stats.""" + return MongoDBSource( + name="driver_stats", + database="feast_test", + collection="driver_stats", + timestamp_field="event_timestamp", + ) + + +@pytest.fixture +def driver_fv(driver_source: MongoDBSource) -> FeatureView: + """Create a FeatureView for driver stats. + + The ttl (time-to-live) parameter defines how far back in time Feast will look + for feature values during point-in-time joins. If a feature's event_timestamp + is older than (entity_timestamp - ttl), that feature value is considered stale + and will be returned as NULL. + + This is different from MongoDB TTL indexes which automatically delete documents + after a period of time. Feast TTL is a query-time filter, not a storage policy. + """ + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + return FeatureView( + name="driver_stats", + entities=[driver_entity], + schema=[ + # Include entity column in schema so entity_columns is populated + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + Field(name="acc_rate", dtype=Float64), + ], + source=driver_source, + ttl=timedelta(days=1), + ) + + +@_requires_docker +def test_pull_latest_from_table_or_query( + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSource +) -> None: + """Test pulling latest features per entity from MongoDB. + + This test verifies that pull_latest returns only the most recent feature + values for each entity (driver_id), even when entities have different + latest timestamps. Driver 1 has data at now, but driver 2's latest data + is from 2 hours ago. + """ + now = sample_data + job = MongoDBOfflineStoreIbis.pull_latest_from_table_or_query( + config=repo_config, + data_source=driver_source, + join_key_columns=["driver_id"], + feature_name_columns=["conv_rate", "acc_rate"], + timestamp_field="event_timestamp", + created_timestamp_column=None, + start_date=now - timedelta(days=1), + end_date=now + timedelta(hours=1), + ) + + df = job.to_df() + + # Validate DataFrame structure + assert isinstance(df, pd.DataFrame) + assert set(df.columns) == {"driver_id", "conv_rate", "acc_rate", "event_timestamp"} + assert len(df) == 2 # Two unique drivers + + # Extract rows for each driver + driver1_rows = df[df["driver_id"] == 1] + driver2_rows = df[df["driver_id"] == 2] + + # Each driver should have exactly one row (the latest) + assert len(driver1_rows) == 1 + assert len(driver2_rows) == 1 + + driver1 = driver1_rows.iloc[0] + driver2 = driver2_rows.iloc[0] + + # Validate types + assert isinstance(driver1["conv_rate"], float) + assert isinstance(driver1["acc_rate"], float) + + # Driver 1's latest values (from "now") + assert driver1["conv_rate"] == pytest.approx(0.7) + assert driver1["acc_rate"] == pytest.approx(0.8) + + # Driver 2's latest values (from 2 hours ago - driver 2 has no "now" data) + # This demonstrates that pull_latest correctly handles entities with + # different "latest" timestamps + assert driver2["conv_rate"] == pytest.approx(0.3) + assert driver2["acc_rate"] == pytest.approx(0.95) + + +@_requires_docker +def test_get_historical_features_pit_join( + repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView +) -> None: + """Test point-in-time join retrieves correct feature values. + + Point-in-time (PIT) join ensures that for each entity row, we get the + feature values that were valid AT THAT POINT IN TIME - not future data + that would cause data leakage in ML training. + """ + now = sample_data + + # Entity dataframe: request features at specific timestamps + # Each row says "give me driver X's features as they were at time T" + entity_df = pd.DataFrame( + { + "driver_id": [1, 1, 2], + "event_timestamp": [ + now + - timedelta( + hours=1, minutes=30 + ), # Should get conv_rate=0.5 (before 0.6 was written) + now + - timedelta( + minutes=30 + ), # Should get conv_rate=0.6 (before 0.7 was written) + now + - timedelta(hours=1), # Should get conv_rate=0.3 (only data available) + ], + } + ) + + job = MongoDBOfflineStoreIbis.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df() + assert isinstance(result_df, pd.DataFrame) + assert len(result_df) == 3 + + # Sort by driver_id and event_timestamp for predictable assertions + result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( + drop=True + ) + + # Driver 1, first request (1.5 hours ago) → should get value from 2 hours ago + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) + + # Driver 1, second request (30 min ago) → should get value from 1 hour ago + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) + + # Driver 2, request (1 hour ago) → should get value from 2 hours ago + assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) + + +@_requires_docker +def test_pull_all_from_table_or_query( + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSource +) -> None: + """Test pulling all features within a time range (no deduplication).""" + now = sample_data + job = MongoDBOfflineStoreIbis.pull_all_from_table_or_query( + config=repo_config, + data_source=driver_source, + join_key_columns=["driver_id"], + feature_name_columns=["conv_rate", "acc_rate"], + timestamp_field="event_timestamp", + created_timestamp_column=None, + start_date=now - timedelta(hours=1, minutes=30), + end_date=now + timedelta(hours=1), + ) + + df = job.to_df() + assert isinstance(df, pd.DataFrame) + # Should get 2 rows: driver 1 (1hr ago, now) + # Excludes: driver 1 row from 2 hours ago (before start_date) + # driver 2 row from 2 hours ago (before start_date) + assert len(df) == 2 + + +@_requires_docker +def test_ttl_excludes_stale_features( + repo_config: RepoConfig, + mongodb_connection_string: str, + driver_source: MongoDBSource, +) -> None: + """Test that TTL causes stale feature values to be returned as NULL. + + Feast TTL (time-to-live) is a query-time filter: if a feature's event_timestamp + is older than (entity_timestamp - ttl), that feature is considered stale. + This is different from MongoDB TTL indexes which delete documents. + """ + # Insert data with a very old timestamp + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["driver_stats_ttl_test"] + collection.drop() + + now = datetime.now(tz=pytz.UTC) + docs = [ + # Fresh data (within TTL) + {"driver_id": 1, "conv_rate": 0.9, "event_timestamp": now - timedelta(hours=1)}, + # Stale data (outside 1-day TTL when queried from "now") + {"driver_id": 2, "conv_rate": 0.5, "event_timestamp": now - timedelta(days=2)}, + ] + collection.insert_many(docs) + client.close() + + # Create source and feature view with 1-day TTL + ttl_source = MongoDBSource( + name="driver_stats_ttl_test", + database="feast_test", + collection="driver_stats_ttl_test", + timestamp_field="event_timestamp", + ) + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + ttl_fv = FeatureView( + name="driver_stats_ttl_test", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=ttl_source, + ttl=timedelta(days=1), # Features older than 1 day are stale + ) + + # Request features "as of now" for both drivers + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStoreIbis.get_historical_features( + config=repo_config, + feature_views=[ttl_fv], + feature_refs=["driver_stats_ttl_test:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + + # Driver 1: fresh data within TTL → should have value + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.9) + + # Driver 2: stale data outside TTL → should be NULL + assert pd.isna(result_df.loc[1, "conv_rate"]) From c940d0572cd4e919abe9ec2e7d6cca2c3b08c3bd Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 17 Mar 2026 12:42:09 -0400 Subject: [PATCH 06/76] Added test of multiple feature views and compound join keys Signed-off-by: Casey Clements --- .../contrib/test_mongodb_offline_retrieval.py | 262 +++++++++++++++++- 1 file changed, 261 insertions(+), 1 deletion(-) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py index cd83e33c0d3..225d18d3e91 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py @@ -27,7 +27,7 @@ MongoDBSource, ) from feast.repo_config import RepoConfig -from feast.types import Float64, Int64 +from feast.types import Float64, Int64, String from feast.value_type import ValueType # Check if Docker is available @@ -386,3 +386,263 @@ def test_ttl_excludes_stale_features( # Driver 2: stale data outside TTL → should be NULL assert pd.isna(result_df.loc[1, "conv_rate"]) + + +@_requires_docker +def test_multiple_feature_views( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Test joining features from multiple MongoDB collections/FeatureViews. + + This simulates a real-world scenario where features come from different + data sources (e.g., driver stats from one collection, vehicle stats from another). + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + + # Collection 1: Driver stats + driver_collection = db["driver_stats_multi"] + driver_collection.drop() + now = datetime.now(tz=pytz.UTC) + driver_docs = [ + {"driver_id": 1, "rating": 4.8, "event_timestamp": now - timedelta(hours=1)}, + {"driver_id": 2, "rating": 4.5, "event_timestamp": now - timedelta(hours=1)}, + ] + driver_collection.insert_many(driver_docs) + + # Collection 2: Vehicle stats (same driver_id, different features) + vehicle_collection = db["vehicle_stats_multi"] + vehicle_collection.drop() + vehicle_docs = [ + { + "driver_id": 1, + "vehicle_age": 2, + "mileage": 50000, + "event_timestamp": now - timedelta(hours=1), + }, + { + "driver_id": 2, + "vehicle_age": 5, + "mileage": 120000, + "event_timestamp": now - timedelta(hours=1), + }, + ] + vehicle_collection.insert_many(vehicle_docs) + client.close() + + # Create sources for each collection + driver_source = MongoDBSource( + name="driver_stats_multi", + database="feast_test", + collection="driver_stats_multi", + timestamp_field="event_timestamp", + ) + vehicle_source = MongoDBSource( + name="vehicle_stats_multi", + database="feast_test", + collection="vehicle_stats_multi", + timestamp_field="event_timestamp", + ) + + # Create entities and feature views + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + + driver_fv = FeatureView( + name="driver_stats_multi", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="rating", dtype=Float64), + ], + source=driver_source, + ttl=timedelta(days=1), + ) + + vehicle_fv = FeatureView( + name="vehicle_stats_multi", + entities=[ + driver_entity + ], # todo these two FeatureViews have the same entities list [driver_entity] + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="vehicle_age", dtype=Int64), + Field(name="mileage", dtype=Int64), + ], + source=vehicle_source, + ttl=timedelta(days=1), + ) + + # Entity dataframe requesting features for both drivers + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [now, now], + } + ) + + # Request features from BOTH feature views + job = MongoDBOfflineStoreIbis.get_historical_features( + config=repo_config, + feature_views=[driver_fv, vehicle_fv], + feature_refs=[ + "driver_stats_multi:rating", + "vehicle_stats_multi:vehicle_age", + "vehicle_stats_multi:mileage", + ], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + + # Verify we got features from both collections joined correctly + assert len(result_df) == 2 + assert set(result_df.columns) >= {"driver_id", "rating", "vehicle_age", "mileage"} + + # Driver 1 + assert result_df.loc[0, "rating"] == pytest.approx(4.8) + assert result_df.loc[0, "vehicle_age"] == 2 + assert result_df.loc[0, "mileage"] == 50000 + + # Driver 2 + assert result_df.loc[1, "rating"] == pytest.approx(4.5) + assert result_df.loc[1, "vehicle_age"] == 5 + assert result_df.loc[1, "mileage"] == 120000 + + +@_requires_docker +def test_compound_join_keys( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Test with compound/composite join keys (multiple entity columns). + + This tests scenarios where entities are identified by multiple keys, + e.g., (user_id, device_id) or (store_id, product_id). + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + + # Create collection with compound key (user_id + device_id) + collection = db["user_device_features"] + collection.drop() + now = datetime.now(tz=pytz.UTC) + + # Same user_id can have different device_ids with different features + docs = [ + { + "user_id": 1, + "device_id": "mobile", + "app_opens": 50, + "event_timestamp": now - timedelta(hours=2), + }, + { + "user_id": 1, + "device_id": "mobile", + "app_opens": 55, + "event_timestamp": now - timedelta(hours=1), + }, + { + "user_id": 1, + "device_id": "desktop", + "app_opens": 10, + "event_timestamp": now - timedelta(hours=1), + }, + { + "user_id": 2, + "device_id": "mobile", + "app_opens": 100, + "event_timestamp": now - timedelta(hours=1), + }, + { + "user_id": 2, + "device_id": "tablet", + "app_opens": 25, + "event_timestamp": now - timedelta(hours=1), + }, + ] + collection.insert_many(docs) + client.close() + + # Create source + source = MongoDBSource( + name="user_device_features", + database="feast_test", + collection="user_device_features", + timestamp_field="event_timestamp", + ) + + # Create entities with compound keys + user_entity = Entity( + name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 + ) + device_entity = Entity( + name="device_id", join_keys=["device_id"], value_type=ValueType.STRING + ) + + fv = FeatureView( + name="user_device_features", + entities=[user_entity, device_entity], + schema=[ + Field(name="user_id", dtype=Int64), + Field(name="device_id", dtype=String), + Field(name="app_opens", dtype=Int64), + ], + source=source, + ttl=timedelta(days=1), + ) + + # Test pull_latest: should get one row per unique (user_id, device_id) combination + job = MongoDBOfflineStoreIbis.pull_latest_from_table_or_query( + config=repo_config, + data_source=source, + join_key_columns=["user_id", "device_id"], + feature_name_columns=["app_opens"], + timestamp_field="event_timestamp", + created_timestamp_column=None, + start_date=now - timedelta(days=1), + end_date=now + timedelta(hours=1), + ) + + df = job.to_df() + assert len(df) == 4 # 4 unique (user_id, device_id) combinations + + # Verify user 1, mobile got the LATEST value (55, not 50) + user1_mobile = df[(df["user_id"] == 1) & (df["device_id"] == "mobile")] + assert len(user1_mobile) == 1 + assert user1_mobile.iloc[0]["app_opens"] == 55 + + # Test get_historical_features with compound keys + entity_df = pd.DataFrame( + { + "user_id": [1, 1, 2], + "device_id": ["mobile", "desktop", "tablet"], + "event_timestamp": [now, now, now], + } + ) + + job = MongoDBOfflineStoreIbis.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["user_device_features:app_opens"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df() + assert len(result_df) == 3 + + # Sort for predictable assertions + result_df = result_df.sort_values(["user_id", "device_id"]).reset_index(drop=True) + + # user 1, desktop + assert result_df.loc[0, "app_opens"] == 10 + # user 1, mobile (latest value) + assert result_df.loc[1, "app_opens"] == 55 + # user 2, tablet + assert result_df.loc[2, "app_opens"] == 25 From 37fad384b18767056ed418e7f1e76d34f7c22ef1 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 17 Mar 2026 13:58:58 -0400 Subject: [PATCH 07/76] Initial implementation of native single-collection offline store Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 622 ++++++++++++++++++ .../test_mongodb_offline_retrieval_native.py | 609 +++++++++++++++++ 2 files changed, 1231 insertions(+) create mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py create mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py new file mode 100644 index 00000000000..a6f0a8acfcc --- /dev/null +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -0,0 +1,622 @@ +# Copyright 2026 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Native MongoDB Offline Store Implementation. + +This module implements a MongoDB offline store using native MQL aggregation +pipelines. It uses a single-collection schema where all feature views share +one collection, discriminated by a ``feature_view`` field. + +Schema: + { + "_id": ObjectId(), + "entity_id": "", + "feature_view": "driver_stats", + "features": { + "rating": 4.91, + "trips_last_7d": 132 + }, + "event_timestamp": ISODate("2026-01-20T12:00:00Z"), + "created_at": ISODate("2026-01-20T12:00:05Z") + } + +Recommended Index: + db.feature_history.create_index([ + ("entity_id", ASCENDING), + ("feature_view", ASCENDING), + ("event_timestamp", DESCENDING), + ]) +""" + +import json +import warnings +from datetime import datetime, timezone +from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union + +import pandas as pd +import pyarrow + +try: + from pymongo import MongoClient +except ImportError: + MongoClient = None # type: ignore[assignment,misc] + +from pydantic import StrictStr + +from feast.data_source import DataSource +from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError +from feast.feature_view import FeatureView +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.offline_store import ( + OfflineStore, + RetrievalJob, + RetrievalMetadata, +) +from feast.infra.offline_stores.offline_utils import ( + infer_event_timestamp_from_entity_df, +) +from feast.infra.registry.base_registry import BaseRegistry +from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import FeastConfigBaseModel, RepoConfig +from feast.type_map import mongodb_to_feast_value_type +from feast.value_type import ValueType + + +class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): + """Configuration for the Native MongoDB offline store. + + Uses a single shared collection for all feature views, with documents + containing an ``entity_id``, ``feature_view`` discriminator, and nested + ``features`` subdocument. + """ + + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBOfflineStoreNative" + """Offline store type selector""" + + connection_string: StrictStr = "mongodb://localhost:27017" + """MongoDB connection URI""" + + database: StrictStr = "feast" + """MongoDB database name""" + + collection: StrictStr = "feature_history" + """Single collection name for all feature views""" + + +class MongoDBSourceNative(DataSource): + """A MongoDB data source for the Native offline store. + + Unlike MongoDBSource (Ibis), this source does not specify a collection + per FeatureView. Instead, all FeatureViews share a single collection + (configured at the store level), and are discriminated by the + ``feature_view`` field in each document. + + The ``name`` parameter becomes the ``feature_view`` discriminator value + used to filter documents in queries. + """ + + def __init__( + self, + name: Optional[str] = None, + timestamp_field: str = "event_timestamp", + created_timestamp_column: str = "created_at", + field_mapping: Optional[Dict[str, str]] = None, + description: Optional[str] = "", + tags: Optional[Dict[str, str]] = None, + owner: Optional[str] = "", + ): + if name is None: + raise DataSourceNoNameException() + + super().__init__( + name=name, + timestamp_field=timestamp_field, + created_timestamp_column=created_timestamp_column, + field_mapping=field_mapping, + description=description, + tags=tags, + owner=owner, + ) + + def __hash__(self): + return super().__hash__() + + def __eq__(self, other): + if not isinstance(other, MongoDBSourceNative): + raise TypeError( + "Comparisons should only involve MongoDBSourceNative class objects." + ) + return ( + super().__eq__(other) + and self.timestamp_field == other.timestamp_field + and self.created_timestamp_column == other.created_timestamp_column + and self.field_mapping == other.field_mapping + ) + + @property + def feature_view_name(self) -> str: + """The feature_view discriminator value (same as source name).""" + return self.name + + def source_type(self) -> DataSourceProto.SourceType.ValueType: + return DataSourceProto.CUSTOM_SOURCE + + @staticmethod + def from_proto(data_source: DataSourceProto) -> "MongoDBSourceNative": + assert data_source.HasField("custom_options") + return MongoDBSourceNative( + name=data_source.name, + timestamp_field=data_source.timestamp_field, + created_timestamp_column=data_source.created_timestamp_column, + field_mapping=dict(data_source.field_mapping), + description=data_source.description, + tags=dict(data_source.tags), + owner=data_source.owner, + ) + + def _to_proto_impl(self) -> DataSourceProto: + return DataSourceProto( + name=self.name, + type=DataSourceProto.CUSTOM_SOURCE, + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBSourceNative", + field_mapping=self.field_mapping, + custom_options=DataSourceProto.CustomSourceOptions( + configuration=json.dumps({"feature_view": self.name}).encode() + ), + description=self.description, + tags=self.tags, + owner=self.owner, + timestamp_field=self.timestamp_field, + created_timestamp_column=self.created_timestamp_column, + ) + + def validate(self, config: RepoConfig): + pass + + @staticmethod + def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: + return mongodb_to_feast_value_type + + def get_table_query_string(self) -> str: + return f"feature_history[feature_view={self.name}]" + + def get_table_column_names_and_types( + self, config: RepoConfig + ) -> Iterable[Tuple[str, str]]: + """Sample documents to infer feature names and types. + + Queries documents matching this source's feature_view name and + inspects the ``features`` subdocument to determine schema. + """ + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + connection_string = config.offline_store.connection_string + db_name = config.offline_store.database + collection_name = config.offline_store.collection + client: Any = MongoClient(connection_string) + try: + pipeline = [ + {"$match": {"feature_view": self.name}}, + {"$sample": {"size": 100}}, + ] + docs = list(client[db_name][collection_name].aggregate(pipeline)) + finally: + client.close() + + field_type_counts: Dict[str, Dict[str, int]] = {} + for doc in docs: + features = doc.get("features", {}) + for field, value in features.items(): + type_str = _infer_python_type_str(value) + if type_str is None: + continue + field_type_counts.setdefault(field, {}) + field_type_counts[field][type_str] = ( + field_type_counts[field].get(type_str, 0) + 1 + ) + + return [ + (field, max(counts, key=lambda t: counts[t])) + for field, counts in field_type_counts.items() + ] + + +def _infer_python_type_str(value: Any) -> Optional[str]: + """Infer a Feast-compatible type string from a Python value.""" + if value is None: + return None + if isinstance(value, bool): + return "bool" + if isinstance(value, int): + return "int" + if isinstance(value, float): + return "float" + if isinstance(value, str): + return "str" + if isinstance(value, bytes): + return "bytes" + if isinstance(value, datetime): + return "datetime" + if isinstance(value, list): + if not value: + return "list[str]" + elem_type = _infer_python_type_str(value[0]) + if elem_type: + return f"list[{elem_type}]" + return "list[str]" + return None + + +def _fetch_documents( + connection_string: str, + database: str, + collection: str, + pipeline: List[Dict], +) -> List[Dict]: + """Execute an aggregation pipeline and return documents.""" + if MongoClient is None: + raise FeastExtrasDependencyImportError("mongodb", "pymongo is not installed.") + client: Any = MongoClient(connection_string) + try: + return list(client[database][collection].aggregate(pipeline)) + finally: + client.close() + + +class MongoDBNativeRetrievalJob(RetrievalJob): + """Retrieval job for native MongoDB offline store queries.""" + + def __init__( + self, + query_fn: Callable[[], pyarrow.Table], + full_feature_names: bool, + on_demand_feature_views: Optional[List[Any]] = None, + metadata: Optional[RetrievalMetadata] = None, + ): + self._query_fn = query_fn + self._full_feature_names = full_feature_names + self._on_demand_feature_views = on_demand_feature_views or [] + self._metadata = metadata + + @property + def full_feature_names(self) -> bool: + return self._full_feature_names + + @property + def on_demand_feature_views(self) -> List[Any]: + return self._on_demand_feature_views + + def _to_df_internal(self, timeout: Optional[int] = None) -> pd.DataFrame: + return self._to_arrow_internal(timeout).to_pandas() + + def _to_arrow_internal(self, timeout: Optional[int] = None) -> pyarrow.Table: + return self._query_fn() + + @property + def metadata(self) -> Optional[RetrievalMetadata]: + return self._metadata + + def persist( + self, + storage: Any, + allow_overwrite: bool = False, + timeout: Optional[int] = None, + ) -> None: + # TODO: Implement persist for native store + raise NotImplementedError("persist() not yet implemented for native store") + + +def _serialize_entity_key_from_row( + row: pd.Series, join_keys: List[str], entity_key_serialization_version: int +) -> bytes: + """Serialize entity key from a DataFrame row.""" + entity_key = EntityKeyProto() + for key in sorted(join_keys): + entity_key.join_keys.append(key) + value = row[key] + val = ValueProto() + if isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + elif isinstance(value, float): + val.double_val = value + else: + val.string_val = str(value) + entity_key.entity_values.append(val) + return serialize_entity_key(entity_key, entity_key_serialization_version) + + +class MongoDBOfflineStoreNative(OfflineStore): + """Native MongoDB offline store using single-collection schema. + + All feature views share one collection (``feature_history``), with documents + containing: + - ``entity_id``: serialized entity key (bytes) + - ``feature_view``: discriminator field matching FeatureView name + - ``features``: subdocument with feature name/value pairs + - ``event_timestamp``: event time + - ``created_at``: ingestion time + """ + + @staticmethod + def pull_latest_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str], + start_date: datetime, + end_date: datetime, + ) -> RetrievalJob: + if not isinstance(data_source, MongoDBSourceNative): + raise ValueError( + f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " + f"got {type(data_source).__name__!r}." + ) + warnings.warn( + "MongoDB offline store (native) is in preview. API may change without notice.", + RuntimeWarning, + ) + + connection_string = config.offline_store.connection_string + db_name = config.offline_store.database + collection = config.offline_store.collection + feature_view_name = data_source.feature_view_name + + start_utc = start_date.astimezone(tz=timezone.utc) + end_utc = end_date.astimezone(tz=timezone.utc) + + # Build aggregation pipeline + pipeline: List[Dict[str, Any]] = [ + { + "$match": { + "feature_view": feature_view_name, + "event_timestamp": {"$gte": start_utc, "$lte": end_utc}, + } + }, + {"$sort": {"entity_id": 1, "event_timestamp": -1}}, + { + "$group": { + "_id": "$entity_id", + "doc": {"$first": "$$ROOT"}, + } + }, + ] + + def _run() -> pyarrow.Table: + docs = _fetch_documents(connection_string, db_name, collection, pipeline) + if not docs: + return pyarrow.Table.from_pydict({}) + + # Flatten documents + rows = [] + for d in docs: + doc = d["doc"] + row = { + "entity_id": doc["entity_id"], + "event_timestamp": doc["event_timestamp"], + } + features = doc.get("features", {}) + for feat in feature_name_columns: + row[feat] = features.get(feat) + rows.append(row) + + df = pd.DataFrame(rows) + # Ensure timestamp is tz-aware + if not df.empty and df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime(df["event_timestamp"], utc=True) + return pyarrow.Table.from_pandas(df, preserve_index=False) + + return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) + + @staticmethod + def pull_all_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str] = None, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None, + ) -> RetrievalJob: + if not isinstance(data_source, MongoDBSourceNative): + raise ValueError( + f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " + f"got {type(data_source).__name__!r}." + ) + warnings.warn( + "MongoDB offline store (native) is in preview. API may change without notice.", + RuntimeWarning, + ) + + connection_string = config.offline_store.connection_string + db_name = config.offline_store.database + collection = config.offline_store.collection + feature_view_name = data_source.feature_view_name + + # Build match filter + match_filter: Dict[str, Any] = {"feature_view": feature_view_name} + if start_date or end_date: + ts_filter: Dict[str, Any] = {} + if start_date: + ts_filter["$gte"] = start_date.astimezone(tz=timezone.utc) + if end_date: + ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) + match_filter["event_timestamp"] = ts_filter + + pipeline = [{"$match": match_filter}] + + def _run() -> pyarrow.Table: + docs = _fetch_documents(connection_string, db_name, collection, pipeline) + if not docs: + return pyarrow.Table.from_pydict({}) + + rows = [] + for doc in docs: + row = { + "entity_id": doc["entity_id"], + "event_timestamp": doc["event_timestamp"], + } + features = doc.get("features", {}) + for feat in feature_name_columns: + row[feat] = features.get(feat) + rows.append(row) + + df = pd.DataFrame(rows) + if not df.empty and df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime(df["event_timestamp"], utc=True) + return pyarrow.Table.from_pandas(df, preserve_index=False) + + return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) + + @staticmethod + def get_historical_features( + config: RepoConfig, + feature_views: List[FeatureView], + feature_refs: List[str], + entity_df: Union[pd.DataFrame, str], + registry: BaseRegistry, + project: str, + full_feature_names: bool = False, + ) -> RetrievalJob: + if isinstance(entity_df, str): + raise ValueError( + "MongoDBOfflineStoreNative does not support SQL entity_df strings. " + "Pass a pandas DataFrame instead." + ) + warnings.warn( + "MongoDB offline store (native) is in preview. API may change without notice.", + RuntimeWarning, + ) + + connection_string = config.offline_store.connection_string + db_name = config.offline_store.database + collection = config.offline_store.collection + entity_key_version = config.entity_key_serialization_version + + entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) + event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) + + # Map "feature_view:feature" refs → {fv_name: [feature, ...]} + fv_to_features: Dict[str, List[str]] = {} + for ref in feature_refs: + fv_name, feat_name = ref.split(":", 1) + fv_to_features.setdefault(fv_name, []).append(feat_name) + + fv_by_name = {fv.name: fv for fv in feature_views} + + def _run() -> pyarrow.Table: + result = entity_df.copy() + + # Ensure entity timestamp is tz-aware UTC + if result[event_timestamp_col].dt.tz is None: + result[event_timestamp_col] = pd.to_datetime( + result[event_timestamp_col], utc=True + ) + result = result.sort_values(event_timestamp_col) + + # Get join keys from entity_df columns (excluding event_timestamp) + entity_columns = [c for c in result.columns if c != event_timestamp_col] + + # Serialize entity keys for lookup + result["_entity_id"] = result.apply( + lambda row: _serialize_entity_key_from_row( + row, entity_columns, entity_key_version + ), + axis=1, + ) + + for fv_name, features in fv_to_features.items(): + fv = fv_by_name[fv_name] + source = fv.batch_source + if not isinstance(source, MongoDBSourceNative): + raise ValueError( + f"MongoDBOfflineStoreNative: feature view {fv_name!r} has " + f"non-MongoDBSourceNative source ({type(source).__name__!r})." + ) + + # Fetch all documents for this feature view + pipeline = [{"$match": {"feature_view": fv_name}}] + docs = _fetch_documents( + connection_string, db_name, collection, pipeline + ) + + if not docs: + for f in features: + col = f"{fv_name}__{f}" if full_feature_names else f + result[col] = None + continue + + # Build feature DataFrame + feature_rows = [] + for doc in docs: + row = { + "_entity_id": doc["entity_id"], + "_fv_ts": doc["event_timestamp"], + } + feat_data = doc.get("features", {}) + for f in features: + row[f] = feat_data.get(f) + feature_rows.append(row) + + feature_df = pd.DataFrame(feature_rows) + if feature_df["_fv_ts"].dt.tz is None: + feature_df["_fv_ts"] = pd.to_datetime( + feature_df["_fv_ts"], utc=True + ) + feature_df = feature_df.sort_values("_fv_ts") + + # Rename features if full_feature_names + col_rename = { + f: (f"{fv_name}__{f}" if full_feature_names else f) + for f in features + } + feature_df = feature_df.rename(columns=col_rename) + out_features = list(col_rename.values()) + + # Point-in-time join using merge_asof + merged = pd.merge_asof( + result, + feature_df, + left_on=event_timestamp_col, + right_on="_fv_ts", + by="_entity_id", + direction="backward", + ) + + # Apply TTL: null out stale features + if fv.ttl: + cutoff = merged[event_timestamp_col] - fv.ttl + too_old = merged["_fv_ts"] < cutoff + for col in out_features: + merged.loc[too_old, col] = None + + result = merged.drop(columns=["_fv_ts"], errors="ignore") + + # Remove internal entity_id column + result = result.drop(columns=["_entity_id"], errors="ignore") + return pyarrow.Table.from_pandas(result, preserve_index=False) + + return MongoDBNativeRetrievalJob( + query_fn=_run, + full_feature_names=full_feature_names, + ) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py new file mode 100644 index 00000000000..5c022992548 --- /dev/null +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py @@ -0,0 +1,609 @@ +""" +Unit tests for MongoDB Native offline store implementation. + +This tests the single-collection schema where all feature views share one +collection (``feature_history``), discriminated by ``feature_view`` field. + +Schema: + { + "entity_id": bytes, # serialized entity key + "feature_view": str, + "features": { "feat1": val, ... }, + "event_timestamp": datetime, + "created_at": datetime + } + +Docker-dependent tests are marked with ``@_requires_docker`` and are skipped +when Docker is unavailable. +""" + +from datetime import datetime, timedelta +from typing import Generator +from unittest.mock import MagicMock + +import pandas as pd +import pytest +import pytz + +pytest.importorskip("pymongo") + +from pymongo import MongoClient +from testcontainers.mongodb import MongoDbContainer + +from feast import Entity, FeatureView, Field +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( + MongoDBOfflineStoreNative, + MongoDBOfflineStoreNativeConfig, + MongoDBSourceNative, +) +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import RepoConfig +from feast.types import Float64, Int64, String +from feast.value_type import ValueType + +# Check if Docker is available +docker_available = False +try: + import docker + + try: + client = docker.from_env() + client.ping() + docker_available = True + except Exception: + pass +except ImportError: + pass + +_requires_docker = pytest.mark.skipif( + not docker_available, + reason="Docker is not available or not running.", +) + +ENTITY_KEY_VERSION = 3 + + +def _make_entity_id(join_keys: dict) -> bytes: + """Create serialized entity key from join key dict.""" + entity_key = EntityKeyProto() + for key in sorted(join_keys.keys()): + entity_key.join_keys.append(key) + val = ValueProto() + value = join_keys[key] + if isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + else: + val.string_val = str(value) + entity_key.entity_values.append(val) + return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) + + +@pytest.fixture(scope="module") +def mongodb_container() -> Generator[MongoDbContainer, None, None]: + """Start a MongoDB container for testing.""" + container = MongoDbContainer( + "mongo:latest", + username="test", + password="test", # pragma: allowlist secret + ).with_exposed_ports(27017) + container.start() + yield container + container.stop() + + +@pytest.fixture +def mongodb_connection_string(mongodb_container: MongoDbContainer) -> str: + """Get MongoDB connection string from the container.""" + exposed_port = mongodb_container.get_exposed_port(27017) + return f"mongodb://test:test@localhost:{exposed_port}" # pragma: allowlist secret + + +@pytest.fixture +def repo_config(mongodb_connection_string: str) -> RepoConfig: + """Create a RepoConfig with MongoDB Native offline store.""" + return RepoConfig( + project="test_project", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreNativeConfig( + connection_string=mongodb_connection_string, + database="feast_test", + collection="feature_history", + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + + +@pytest.fixture +def sample_data(mongodb_connection_string: str) -> datetime: + """Insert sample data using the single-collection schema. + + Creates documents for 'driver_stats' feature view with entity_id, + feature_view discriminator, and nested features subdocument. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + collection.drop() + + now = datetime.now(tz=pytz.UTC) + + # Create documents using the native schema + docs = [ + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats", + "features": {"conv_rate": 0.5, "acc_rate": 0.9}, + "event_timestamp": now - timedelta(hours=2), + "created_at": now - timedelta(hours=2), + }, + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats", + "features": {"conv_rate": 0.6, "acc_rate": 0.85}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats", + "features": {"conv_rate": 0.7, "acc_rate": 0.8}, + "event_timestamp": now, + "created_at": now, + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats", + "features": {"conv_rate": 0.3, "acc_rate": 0.95}, + "event_timestamp": now - timedelta(hours=2), + "created_at": now - timedelta(hours=2), + }, + ] + collection.insert_many(docs) + client.close() + return now + + +@pytest.fixture +def driver_source() -> MongoDBSourceNative: + """Create a MongoDBSourceNative for driver stats.""" + return MongoDBSourceNative( + name="driver_stats", + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + ) + + +@pytest.fixture +def driver_fv(driver_source: MongoDBSourceNative) -> FeatureView: + """Create a FeatureView for driver stats.""" + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + return FeatureView( + name="driver_stats", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + Field(name="acc_rate", dtype=Float64), + ], + source=driver_source, + ttl=timedelta(days=1), + ) + + +@_requires_docker +def test_pull_latest_from_table_or_query( + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceNative +) -> None: + """Test pulling latest features per entity from the single collection.""" + now = sample_data + job = MongoDBOfflineStoreNative.pull_latest_from_table_or_query( + config=repo_config, + data_source=driver_source, + join_key_columns=["driver_id"], + feature_name_columns=["conv_rate", "acc_rate"], + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + start_date=now - timedelta(days=1), + end_date=now + timedelta(hours=1), + ) + + df = job.to_df() + + assert isinstance(df, pd.DataFrame) + assert len(df) == 2 # Two unique entity_ids + + # Sort by entity_id for predictable assertions + # Note: entity_id is bytes, so we check features directly + conv_rates = sorted(df["conv_rate"].tolist()) + assert conv_rates[0] == pytest.approx(0.3) # Driver 2's only value + assert conv_rates[1] == pytest.approx(0.7) # Driver 1's latest value + + +@_requires_docker +def test_get_historical_features_pit_join( + repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView +) -> None: + """Test point-in-time join retrieves correct feature values.""" + now = sample_data + + # Entity dataframe with driver_id column (must match join keys) + entity_df = pd.DataFrame( + { + "driver_id": [1, 1, 2], + "event_timestamp": [ + now - timedelta(hours=1, minutes=30), # Should get conv_rate=0.5 + now - timedelta(minutes=30), # Should get conv_rate=0.6 + now - timedelta(hours=1), # Should get conv_rate=0.3 + ], + } + ) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df() + assert isinstance(result_df, pd.DataFrame) + assert len(result_df) == 3 + + # Sort by driver_id and event_timestamp for predictable assertions + result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( + drop=True + ) + + # Driver 1, first request (1.5 hours ago) → should get value from 2 hours ago + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) + + # Driver 1, second request (30 min ago) → should get value from 1 hour ago + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) + + # Driver 2, request (1 hour ago) → should get value from 2 hours ago + assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) + + +@_requires_docker +def test_pull_all_from_table_or_query( + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceNative +) -> None: + """Test pulling all features within a time range (no deduplication).""" + now = sample_data + job = MongoDBOfflineStoreNative.pull_all_from_table_or_query( + config=repo_config, + data_source=driver_source, + join_key_columns=["driver_id"], + feature_name_columns=["conv_rate", "acc_rate"], + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + start_date=now - timedelta(hours=1, minutes=30), + end_date=now + timedelta(hours=1), + ) + + df = job.to_df() + assert isinstance(df, pd.DataFrame) + # Should get 2 rows: driver 1 (1hr ago, now) + # Excludes: driver 1 from 2 hours ago, driver 2 from 2 hours ago + assert len(df) == 2 + + +@_requires_docker +def test_ttl_excludes_stale_features( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Test that TTL causes stale feature values to be returned as NULL.""" + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + + now = datetime.now(tz=pytz.UTC) + + # Insert docs with different ages + ttl_docs = [ + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_ttl", + "features": {"conv_rate": 0.9}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats_ttl", + "features": {"conv_rate": 0.5}, + "event_timestamp": now - timedelta(days=2), # Stale + "created_at": now - timedelta(days=2), + }, + ] + collection.insert_many(ttl_docs) + client.close() + + ttl_source = MongoDBSourceNative( + name="driver_stats_ttl", + timestamp_field="event_timestamp", + ) + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + ttl_fv = FeatureView( + name="driver_stats_ttl", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=ttl_source, + ttl=timedelta(days=1), + ) + + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[ttl_fv], + feature_refs=["driver_stats_ttl:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + + # Driver 1: fresh → has value + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.9) + + # Driver 2: stale → NULL + assert pd.isna(result_df.loc[1, "conv_rate"]) + + +@_requires_docker +def test_multiple_feature_views( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Test joining features from multiple feature views in the same collection.""" + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + + now = datetime.now(tz=pytz.UTC) + + # Insert documents for two different feature views + multi_docs = [ + # driver_stats_multi + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_multi", + "features": {"rating": 4.8}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats_multi", + "features": {"rating": 4.5}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + # vehicle_stats_multi + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "vehicle_stats_multi", + "features": {"vehicle_age": 2, "mileage": 50000}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "vehicle_stats_multi", + "features": {"vehicle_age": 5, "mileage": 120000}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + ] + collection.insert_many(multi_docs) + client.close() + + # Create sources and feature views + driver_source = MongoDBSourceNative(name="driver_stats_multi") + vehicle_source = MongoDBSourceNative(name="vehicle_stats_multi") + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + + driver_fv = FeatureView( + name="driver_stats_multi", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="rating", dtype=Float64), + ], + source=driver_source, + ttl=timedelta(days=1), + ) + + vehicle_fv = FeatureView( + name="vehicle_stats_multi", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="vehicle_age", dtype=Int64), + Field(name="mileage", dtype=Int64), + ], + source=vehicle_source, + ttl=timedelta(days=1), + ) + + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv, vehicle_fv], + feature_refs=[ + "driver_stats_multi:rating", + "vehicle_stats_multi:vehicle_age", + "vehicle_stats_multi:mileage", + ], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + + assert len(result_df) == 2 + assert set(result_df.columns) >= {"driver_id", "rating", "vehicle_age", "mileage"} + + # Driver 1 + assert result_df.loc[0, "rating"] == pytest.approx(4.8) + assert result_df.loc[0, "vehicle_age"] == 2 + assert result_df.loc[0, "mileage"] == 50000 + + # Driver 2 + assert result_df.loc[1, "rating"] == pytest.approx(4.5) + assert result_df.loc[1, "vehicle_age"] == 5 + assert result_df.loc[1, "mileage"] == 120000 + + +@_requires_docker +def test_compound_join_keys( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Test with compound/composite join keys (multiple entity columns).""" + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + + now = datetime.now(tz=pytz.UTC) + + # Insert documents with compound keys (user_id + device_id) + compound_docs = [ + { + "entity_id": _make_entity_id({"user_id": 1, "device_id": "mobile"}), + "feature_view": "user_device_features", + "features": {"app_opens": 50}, + "event_timestamp": now - timedelta(hours=2), + "created_at": now - timedelta(hours=2), + }, + { + "entity_id": _make_entity_id({"user_id": 1, "device_id": "mobile"}), + "feature_view": "user_device_features", + "features": {"app_opens": 55}, # Latest for this entity + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"user_id": 1, "device_id": "desktop"}), + "feature_view": "user_device_features", + "features": {"app_opens": 10}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"user_id": 2, "device_id": "tablet"}), + "feature_view": "user_device_features", + "features": {"app_opens": 25}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + ] + collection.insert_many(compound_docs) + client.close() + + source = MongoDBSourceNative(name="user_device_features") + + user_entity = Entity( + name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 + ) + device_entity = Entity( + name="device_id", join_keys=["device_id"], value_type=ValueType.STRING + ) + + fv = FeatureView( + name="user_device_features", + entities=[user_entity, device_entity], + schema=[ + Field(name="user_id", dtype=Int64), + Field(name="device_id", dtype=String), + Field(name="app_opens", dtype=Int64), + ], + source=source, + ttl=timedelta(days=1), + ) + + # Test pull_latest: should get one row per unique (user_id, device_id) + job = MongoDBOfflineStoreNative.pull_latest_from_table_or_query( + config=repo_config, + data_source=source, + join_key_columns=["user_id", "device_id"], + feature_name_columns=["app_opens"], + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + start_date=now - timedelta(days=1), + end_date=now + timedelta(hours=1), + ) + + df = job.to_df() + assert len(df) == 3 # 3 unique (user_id, device_id) combinations + + # Verify we got the latest value (55) for user 1, mobile + app_opens_values = sorted(df["app_opens"].tolist()) + assert 55 in app_opens_values # Latest for user 1, mobile + assert 10 in app_opens_values # user 1, desktop + assert 25 in app_opens_values # user 2, tablet + + # Test get_historical_features with compound keys + entity_df = pd.DataFrame( + { + "user_id": [1, 1, 2], + "device_id": ["mobile", "desktop", "tablet"], + "event_timestamp": [now, now, now], + } + ) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["user_device_features:app_opens"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df() + assert len(result_df) == 3 + + # Sort for predictable assertions + result_df = result_df.sort_values(["user_id", "device_id"]).reset_index(drop=True) + + # user 1, desktop + assert result_df.loc[0, "app_opens"] == 10 + # user 1, mobile (latest value) + assert result_df.loc[1, "app_opens"] == 55 + # user 2, tablet + assert result_df.loc[2, "app_opens"] == 25 From 67c39d4324959882c575e76cfc1b3316d23ff181 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 18 Mar 2026 11:30:42 -0400 Subject: [PATCH 08/76] Added DriverInfo to MongoDBClients Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/__init__.py | 7 +++++++ .../contrib/mongodb_offline_store/mongodb.py | 17 ++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py index 8b137891791..535583bc38d 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py @@ -1 +1,8 @@ +import feast.version +try: + from pymongo.driver_info import DriverInfo + + DRIVER_METADATA = DriverInfo(name="Feast", version=feast.version.get_version()) +except ImportError: + DRIVER_METADATA = None # type: ignore[assignment] diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index ee37b11c418..51100ef8270 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -33,6 +33,7 @@ SavedDatasetLocationAlreadyExists, ) from feast.feature_view import FeatureView +from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source import ( MongoDBSource, ) @@ -178,7 +179,7 @@ def reader(data_source: DataSource, repo_path: str) -> Table: ) connection_string = config.offline_store.connection_string db_name = data_source.database or config.offline_store.database - client: Any = MongoClient(connection_string) + client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) try: docs = list(client[db_name][data_source.collection].find({}, {"_id": 0})) finally: @@ -230,7 +231,9 @@ def writer( connection_string = config.offline_store.connection_string db_name = data_source.database or config.offline_store.database location = f"{db_name}.{data_source.collection}" - client: Any = MongoClient(connection_string, tz_aware=True) + client: Any = MongoClient( + connection_string, driver=DRIVER_METADATA, tz_aware=True + ) try: coll = client[db_name][data_source.collection] if mode == "overwrite": @@ -277,7 +280,7 @@ def _fetch_collection_as_arrow( """ if MongoClient is None: raise FeastExtrasDependencyImportError("mongodb", "pymongo is not installed.") - client: Any = MongoClient(connection_string, tz_aware=True) + client: Any = MongoClient(connection_string, driver=DRIVER_METADATA, tz_aware=True) try: if pipeline is not None: docs = list(client[db_name][collection].aggregate(pipeline)) @@ -355,7 +358,9 @@ def persist( connection_string = self._config.offline_store.connection_string db_name = data_source.database or self._config.offline_store.database location = f"{db_name}.{data_source.collection}" - client: Any = MongoClient(connection_string, tz_aware=True) + client: Any = MongoClient( + connection_string, driver=DRIVER_METADATA, tz_aware=True + ) try: coll = client[db_name][data_source.collection] if not allow_overwrite and coll.estimated_document_count() > 0: @@ -400,7 +405,9 @@ def offline_write_batch( connection_string = config.offline_store.connection_string db_name = data_source.database or config.offline_store.database records = table.to_pylist() - client: Any = MongoClient(connection_string, tz_aware=True) + client: Any = MongoClient( + connection_string, driver=DRIVER_METADATA, tz_aware=True + ) try: coll = client[db_name][data_source.collection] if records: From 79874056c91de97bba7108ce4031e09bd8b53c95 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 18 Mar 2026 14:29:38 -0400 Subject: [PATCH 09/76] Optimized MQL. Applied FV-level TTL Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 307 ++++++++++++------ 1 file changed, 203 insertions(+), 104 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py index a6f0a8acfcc..0b0dfcc06f2 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -41,6 +41,7 @@ """ import json +import uuid import warnings from datetime import datetime, timezone from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union @@ -59,6 +60,7 @@ from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError from feast.feature_view import FeatureView from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA from feast.infra.offline_stores.offline_store import ( OfflineStore, RetrievalJob, @@ -209,7 +211,7 @@ def get_table_column_names_and_types( connection_string = config.offline_store.connection_string db_name = config.offline_store.database collection_name = config.offline_store.collection - client: Any = MongoClient(connection_string) + client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) try: pipeline = [ {"$match": {"feature_view": self.name}}, @@ -272,7 +274,7 @@ def _fetch_documents( """Execute an aggregation pipeline and return documents.""" if MongoClient is None: raise FeastExtrasDependencyImportError("mongodb", "pymongo is not installed.") - client: Any = MongoClient(connection_string) + client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) try: return list(client[database][collection].aggregate(pipeline)) finally: @@ -343,6 +345,55 @@ def _serialize_entity_key_from_row( return serialize_entity_key(entity_key, entity_key_serialization_version) +def _ttl_to_ms(fv: FeatureView) -> Optional[int]: + """Convert FeatureView TTL to milliseconds.""" + if fv.ttl is None: + return None + return int(fv.ttl.total_seconds() * 1000) + + +def _build_ttl_gte_expr(feature_views: List[FeatureView]) -> Optional[Dict[str, Any]]: + """Build a $gte expression with per-FV TTL using $switch. + + Returns a MongoDB expression that evaluates to: + event_timestamp >= (entity_timestamp - ttl_for_this_feature_view) + + Each feature_view can have a different TTL, handled via $switch branches. + If no feature views have TTL, returns None (no filtering needed). + """ + branches = [] + + for fv in feature_views: + ttl_ms = _ttl_to_ms(fv) + if ttl_ms is None: + # No TTL for this FV - skip (effectively infinite history) + continue + + branches.append( + { + "case": {"$eq": ["$feature_view", fv.name]}, + "then": {"$subtract": ["$$ts", ttl_ms]}, + } + ) + + # If no TTLs at all, no lower bound needed + if not branches: + return None + + return { + "$gte": [ + "$event_timestamp", + { + "$switch": { + "branches": branches, + # Default: no lower bound (for FVs without TTL) + "default": {"$literal": 0}, + } + }, + ] + } + + class MongoDBOfflineStoreNative(OfflineStore): """Native MongoDB offline store using single-collection schema. @@ -384,6 +435,17 @@ def pull_latest_from_table_or_query( start_utc = start_date.astimezone(tz=timezone.utc) end_utc = end_date.astimezone(tz=timezone.utc) + # Build projection to flatten features subdoc to top-level fields + project_stage: Dict[str, Any] = { + "_id": 0, + "entity_id": "$doc.entity_id", + "event_timestamp": "$doc.event_timestamp", + } + if created_timestamp_column: + project_stage["created_at"] = "$doc.created_at" + for feat in feature_name_columns: + project_stage[feat] = f"$doc.features.{feat}" + # Build aggregation pipeline pipeline: List[Dict[str, Any]] = [ { @@ -399,6 +461,7 @@ def pull_latest_from_table_or_query( "doc": {"$first": "$$ROOT"}, } }, + {"$project": project_stage}, ] def _run() -> pyarrow.Table: @@ -406,23 +469,12 @@ def _run() -> pyarrow.Table: if not docs: return pyarrow.Table.from_pydict({}) - # Flatten documents - rows = [] - for d in docs: - doc = d["doc"] - row = { - "entity_id": doc["entity_id"], - "event_timestamp": doc["event_timestamp"], - } - features = doc.get("features", {}) - for feat in feature_name_columns: - row[feat] = features.get(feat) - rows.append(row) - - df = pd.DataFrame(rows) - # Ensure timestamp is tz-aware - if not df.empty and df["event_timestamp"].dt.tz is None: - df["event_timestamp"] = pd.to_datetime(df["event_timestamp"], utc=True) + df = pd.DataFrame(docs) + if not df.empty and "event_timestamp" in df.columns: + if df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime( + df["event_timestamp"], utc=True + ) return pyarrow.Table.from_pandas(df, preserve_index=False) return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) @@ -453,7 +505,7 @@ def pull_all_from_table_or_query( collection = config.offline_store.collection feature_view_name = data_source.feature_view_name - # Build match filter + # Build match filter: feature_view + optional time range match_filter: Dict[str, Any] = {"feature_view": feature_view_name} if start_date or end_date: ts_filter: Dict[str, Any] = {} @@ -463,27 +515,35 @@ def pull_all_from_table_or_query( ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) match_filter["event_timestamp"] = ts_filter - pipeline = [{"$match": match_filter}] + # Build projection: flatten features subdoc to top-level fields + # This uses $getField to extract each feature from the features subdoc + project_stage: Dict[str, Any] = { + "_id": 0, + "entity_id": 1, + "event_timestamp": 1, + } + if created_timestamp_column: + project_stage["created_at"] = 1 + for feat in feature_name_columns: + project_stage[feat] = f"$features.{feat}" + + # Simple range scan pipeline - no sorting for efficiency + pipeline: List[Dict[str, Any]] = [ + {"$match": match_filter}, + {"$project": project_stage}, + ] def _run() -> pyarrow.Table: docs = _fetch_documents(connection_string, db_name, collection, pipeline) if not docs: return pyarrow.Table.from_pydict({}) - rows = [] - for doc in docs: - row = { - "entity_id": doc["entity_id"], - "event_timestamp": doc["event_timestamp"], - } - features = doc.get("features", {}) - for feat in feature_name_columns: - row[feat] = features.get(feat) - rows.append(row) - - df = pd.DataFrame(rows) - if not df.empty and df["event_timestamp"].dt.tz is None: - df["event_timestamp"] = pd.to_datetime(df["event_timestamp"], utc=True) + df = pd.DataFrame(docs) + if not df.empty and "event_timestamp" in df.columns: + if df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime( + df["event_timestamp"], utc=True + ) return pyarrow.Table.from_pandas(df, preserve_index=False) return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) @@ -510,7 +570,7 @@ def get_historical_features( connection_string = config.offline_store.connection_string db_name = config.offline_store.database - collection = config.offline_store.collection + feature_collection = config.offline_store.collection entity_key_version = config.entity_key_serialization_version entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) @@ -522,22 +582,28 @@ def get_historical_features( fv_name, feat_name = ref.split(":", 1) fv_to_features.setdefault(fv_name, []).append(feat_name) - fv_by_name = {fv.name: fv for fv in feature_views} + fv_names = list(fv_to_features.keys()) + + # Build per-FV TTL expression using $switch + ttl_expr = _build_ttl_gte_expr(feature_views) def _run() -> pyarrow.Table: - result = entity_df.copy() + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) - # Ensure entity timestamp is tz-aware UTC + # Prepare entity_df: ensure timestamps are UTC and serialize entity keys + result = entity_df.copy() if result[event_timestamp_col].dt.tz is None: result[event_timestamp_col] = pd.to_datetime( result[event_timestamp_col], utc=True ) - result = result.sort_values(event_timestamp_col) - # Get join keys from entity_df columns (excluding event_timestamp) + # Get join keys (all columns except event_timestamp) entity_columns = [c for c in result.columns if c != event_timestamp_col] - # Serialize entity keys for lookup + # Serialize entity keys to bytes (same format as online store) result["_entity_id"] = result.apply( lambda row: _serialize_entity_key_from_row( row, entity_columns, entity_key_version @@ -545,76 +611,109 @@ def _run() -> pyarrow.Table: axis=1, ) - for fv_name, features in fv_to_features.items(): - fv = fv_by_name[fv_name] - source = fv.batch_source - if not isinstance(source, MongoDBSourceNative): - raise ValueError( - f"MongoDBOfflineStoreNative: feature view {fv_name!r} has " - f"non-MongoDBSourceNative source ({type(source).__name__!r})." - ) - - # Fetch all documents for this feature view - pipeline = [{"$match": {"feature_view": fv_name}}] - docs = _fetch_documents( - connection_string, db_name, collection, pipeline + # Build temp collection documents + temp_docs = [] + for _, row in result.iterrows(): + temp_docs.append( + { + "entity_id": row["_entity_id"], + "event_timestamp": row[event_timestamp_col], + "_row_idx": _, # Preserve original order + } ) - if not docs: - for f in features: - col = f"{fv_name}__{f}" if full_feature_names else f - result[col] = None - continue + # Create temp collection with unique name + temp_collection_name = f"entity_df_{uuid.uuid4().hex[:12]}" + + client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) + try: + db = client[db_name] + temp_collection = db[temp_collection_name] + temp_collection.insert_many(temp_docs) + + # Build $lookup subpipeline with PIT join logic + # Match: entity_id, feature_view in list, event_timestamp <= entity.ts + match_conditions: List[Dict[str, Any]] = [ + {"$eq": ["$entity_id", "$$entity_id"]}, + {"$in": ["$feature_view", fv_names]}, + {"$lte": ["$event_timestamp", "$$ts"]}, + ] + # Add per-FV TTL filter using $switch + if ttl_expr is not None: + match_conditions.append(ttl_expr) + + lookup_pipeline: List[Dict[str, Any]] = [ + {"$match": {"$expr": {"$and": match_conditions}}}, + {"$sort": {"feature_view": 1, "event_timestamp": -1}}, + { + "$group": { + "_id": "$feature_view", + "doc": {"$first": "$$ROOT"}, + } + }, + ] + + # Main aggregation pipeline + pipeline: List[Dict[str, Any]] = [ + { + "$lookup": { + "from": feature_collection, + "let": { + "entity_id": "$entity_id", + "ts": "$event_timestamp", + }, + "pipeline": lookup_pipeline, + "as": "feature_rows", + } + }, + {"$sort": {"_row_idx": 1}}, # Preserve original order + ] + + docs = list(temp_collection.aggregate(pipeline)) + + finally: + # Cleanup temp collection + client[db_name][temp_collection_name].drop() + client.close() - # Build feature DataFrame - feature_rows = [] - for doc in docs: - row = { - "_entity_id": doc["entity_id"], - "_fv_ts": doc["event_timestamp"], - } - feat_data = doc.get("features", {}) - for f in features: - row[f] = feat_data.get(f) - feature_rows.append(row) - - feature_df = pd.DataFrame(feature_rows) - if feature_df["_fv_ts"].dt.tz is None: - feature_df["_fv_ts"] = pd.to_datetime( - feature_df["_fv_ts"], utc=True - ) - feature_df = feature_df.sort_values("_fv_ts") + if not docs: + return pyarrow.Table.from_pydict({}) - # Rename features if full_feature_names - col_rename = { - f: (f"{fv_name}__{f}" if full_feature_names else f) - for f in features + # Build result DataFrame + rows = [] + for doc in docs: + # Start with entity columns from original entity_df + row_idx = doc["_row_idx"] + row = result.iloc[row_idx][ + entity_columns + [event_timestamp_col] + ].to_dict() + + # Extract features from each feature_view's matched doc + feature_rows_by_fv = { + fr["_id"]: fr["doc"] for fr in doc.get("feature_rows", []) } - feature_df = feature_df.rename(columns=col_rename) - out_features = list(col_rename.values()) - - # Point-in-time join using merge_asof - merged = pd.merge_asof( - result, - feature_df, - left_on=event_timestamp_col, - right_on="_fv_ts", - by="_entity_id", - direction="backward", - ) - # Apply TTL: null out stale features - if fv.ttl: - cutoff = merged[event_timestamp_col] - fv.ttl - too_old = merged["_fv_ts"] < cutoff - for col in out_features: - merged.loc[too_old, col] = None + # Extract features from each feature_view's matched doc + # TTL is already applied server-side via $switch expression + for fv_name, features in fv_to_features.items(): + fv_doc = feature_rows_by_fv.get(fv_name) - result = merged.drop(columns=["_fv_ts"], errors="ignore") + for feat in features: + col_name = f"{fv_name}__{feat}" if full_feature_names else feat + if fv_doc is None: + row[col_name] = None + else: + row[col_name] = fv_doc.get("features", {}).get(feat) - # Remove internal entity_id column - result = result.drop(columns=["_entity_id"], errors="ignore") - return pyarrow.Table.from_pandas(result, preserve_index=False) + rows.append(row) + + result_df = pd.DataFrame(rows) + if not result_df.empty and event_timestamp_col in result_df.columns: + if result_df[event_timestamp_col].dt.tz is None: + result_df[event_timestamp_col] = pd.to_datetime( + result_df[event_timestamp_col], utc=True + ) + return pyarrow.Table.from_pandas(result_df, preserve_index=False) return MongoDBNativeRetrievalJob( query_fn=_run, From adf1fb024a046f42285f317631d38b3c42c6a38a Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 18 Mar 2026 17:10:40 -0400 Subject: [PATCH 10/76] filter TTL by relevant FVs only, cautiously reset df index; add created_at tie-breaker in sort Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb_native.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py index 0b0dfcc06f2..214d5657d39 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -454,7 +454,7 @@ def pull_latest_from_table_or_query( "event_timestamp": {"$gte": start_utc, "$lte": end_utc}, } }, - {"$sort": {"entity_id": 1, "event_timestamp": -1}}, + {"$sort": {"entity_id": 1, "event_timestamp": -1, "created_at": -1}}, { "$group": { "_id": "$entity_id", @@ -585,7 +585,8 @@ def get_historical_features( fv_names = list(fv_to_features.keys()) # Build per-FV TTL expression using $switch - ttl_expr = _build_ttl_gte_expr(feature_views) + relevant_fvs = [fv for fv in feature_views if fv.name in fv_to_features] + ttl_expr = _build_ttl_gte_expr(relevant_fvs) def _run() -> pyarrow.Table: if MongoClient is None: @@ -623,7 +624,7 @@ def _run() -> pyarrow.Table: ) # Create temp collection with unique name - temp_collection_name = f"entity_df_{uuid.uuid4().hex[:12]}" + temp_collection_name = f"tmp_entity_df_{uuid.uuid4().hex[:12]}" client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) try: @@ -680,6 +681,7 @@ def _run() -> pyarrow.Table: return pyarrow.Table.from_pydict({}) # Build result DataFrame + result = result.reset_index(drop=True) rows = [] for doc in docs: # Start with entity columns from original entity_df From ed1571ef2642c78a792ef67c74ae05c663120331 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 18 Mar 2026 17:47:39 -0400 Subject: [PATCH 11/76] Updated docstrings Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 82 +++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py index 214d5657d39..ba2d9e29a04 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -17,9 +17,19 @@ This module implements a MongoDB offline store using native MQL aggregation pipelines. It uses a single-collection schema where all feature views share -one collection, discriminated by a ``feature_view`` field. +one collection. It is event-based: each document represents an observation +of a FeatureView at a specific point in time. Each document may contain a +subset (0 or more) of the features defined in that FeatureView, all sharing +a single event_timestamp. -Schema: +Collection Index: + db.feature_history.create_index([ + ("feature_view", ASCENDING), + ("entity_id", ASCENDING), + ("event_timestamp", DESCENDING), + ]) + +Document Schema (example): { "_id": ObjectId(), "entity_id": "", @@ -32,12 +42,42 @@ "created_at": ISODate("2026-01-20T12:00:05Z") } -Recommended Index: - db.feature_history.create_index([ - ("entity_id", ASCENDING), - ("feature_view", ASCENDING), - ("event_timestamp", DESCENDING), - ]) +Feature Freshness Semantics: + This implementation operates at *document-level freshness*, not + per-feature freshness. During retrieval (e.g. point-in-time joins), + the system selects the most recent document for a given + (entity_id, feature_view) that satisfies time constraints, and then + extracts all requested features from that document. + + As a result, if a newer document contains only a subset of features, + missing features will be returned as NULL—even if older documents + contained values for those features. The system does not backfill + individual feature values from earlier events. + + This behavior matches common Feast offline store semantics, but may + differ from systems that compute "latest value per feature". + +Schema Evolution ("Feature Creep"): + Because features are stored in a flexible subdocument, different + documents for the same FeatureView may contain different sets of + feature fields over time. This supports: + - adding new features without backfilling historical data + - partial writes or sparse feature computation + + However, it also implies: + - newly added features will be NULL for older events + - partially populated documents may lead to NULL values even + when older data contained those features + + Users should ensure that feature computation pipelines write + complete feature sets when consistent availability is required. + +Notes: + - Entity keys are serialized to ensure consistency with Feast’s + online store and to avoid type ambiguity. + - Point-in-time correctness is enforced per FeatureView. + - TTL (time-to-live) constraints are applied per FeatureView during + historical retrieval. """ import json @@ -79,12 +119,7 @@ class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): - """Configuration for the Native MongoDB offline store. - - Uses a single shared collection for all feature views, with documents - containing an ``entity_id``, ``feature_view`` discriminator, and nested - ``features`` subdocument. - """ + """Configuration for the Native MongoDB offline store.""" type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBOfflineStoreNative" """Offline store type selector""" @@ -100,15 +135,16 @@ class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): class MongoDBSourceNative(DataSource): - """A MongoDB data source for the Native offline store. + """A MongoDB data source for the native offline store. - Unlike MongoDBSource (Ibis), this source does not specify a collection - per FeatureView. Instead, all FeatureViews share a single collection - (configured at the store level), and are discriminated by the - ``feature_view`` field in each document. + Unlike many data source implementations, this source does not map each + FeatureView to its own table or collection. Instead, all FeatureViews + share a single MongoDB collection (configured at the store level). - The ``name`` parameter becomes the ``feature_view`` discriminator value - used to filter documents in queries. + Each document in that collection includes a ``feature_view`` field that + identifies which FeatureView it belongs to. The ``name`` of this data + source corresponds to that value and is used to filter documents during + queries. """ def __init__( @@ -400,7 +436,7 @@ class MongoDBOfflineStoreNative(OfflineStore): All feature views share one collection (``feature_history``), with documents containing: - ``entity_id``: serialized entity key (bytes) - - ``feature_view``: discriminator field matching FeatureView name + - ``feature_view``: field matching FeatureView name - ``features``: subdocument with feature name/value pairs - ``event_timestamp``: event time - ``created_at``: ingestion time @@ -623,7 +659,7 @@ def _run() -> pyarrow.Table: } ) - # Create temp collection with unique name + # Create temporary collection for query temp_collection_name = f"tmp_entity_df_{uuid.uuid4().hex[:12]}" client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) From c09faeea6194289445345ca87d01794496c362e8 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 18 Mar 2026 18:21:39 -0400 Subject: [PATCH 12/76] Lazy index creation via _get_client_and_ensure_indexes Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 103 ++++++++++++------ 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py index ba2d9e29a04..c9cbae587a2 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -302,19 +302,13 @@ def _infer_python_type_str(value: Any) -> Optional[str]: def _fetch_documents( - connection_string: str, + client: Any, database: str, collection: str, pipeline: List[Dict], ) -> List[Dict]: """Execute an aggregation pipeline and return documents.""" - if MongoClient is None: - raise FeastExtrasDependencyImportError("mongodb", "pymongo is not installed.") - client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) - try: - return list(client[database][collection].aggregate(pipeline)) - finally: - client.close() + return list(client[database][collection].aggregate(pipeline)) class MongoDBNativeRetrievalJob(RetrievalJob): @@ -442,6 +436,42 @@ class MongoDBOfflineStoreNative(OfflineStore): - ``created_at``: ingestion time """ + _index_initialized: bool = False + + @staticmethod + def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: + """Create recommended indexes on the feature_history collection.""" + collection = client[db_name][collection_name] + collection.create_index( + [ + ("entity_id", 1), + ("feature_view", 1), + ("event_timestamp", -1), + ], + name="entity_fv_ts_idx", + ) + + @classmethod + def _get_client_and_ensure_indexes(cls, config: RepoConfig) -> Any: + """Get a MongoClient and ensure indexes exist (once per process).""" + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + client: Any = MongoClient( + config.offline_store.connection_string, driver=DRIVER_METADATA + ) + + if not cls._index_initialized: + cls._ensure_indexes( + client, + config.offline_store.database, + config.offline_store.collection, + ) + cls._index_initialized = True + + return client + @staticmethod def pull_latest_from_table_or_query( config: RepoConfig, @@ -463,7 +493,6 @@ def pull_latest_from_table_or_query( RuntimeWarning, ) - connection_string = config.offline_store.connection_string db_name = config.offline_store.database collection = config.offline_store.collection feature_view_name = data_source.feature_view_name @@ -501,17 +530,21 @@ def pull_latest_from_table_or_query( ] def _run() -> pyarrow.Table: - docs = _fetch_documents(connection_string, db_name, collection, pipeline) - if not docs: - return pyarrow.Table.from_pydict({}) - - df = pd.DataFrame(docs) - if not df.empty and "event_timestamp" in df.columns: - if df["event_timestamp"].dt.tz is None: - df["event_timestamp"] = pd.to_datetime( - df["event_timestamp"], utc=True - ) - return pyarrow.Table.from_pandas(df, preserve_index=False) + client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + try: + docs = _fetch_documents(client, db_name, collection, pipeline) + if not docs: + return pyarrow.Table.from_pydict({}) + + df = pd.DataFrame(docs) + if not df.empty and "event_timestamp" in df.columns: + if df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime( + df["event_timestamp"], utc=True + ) + return pyarrow.Table.from_pandas(df, preserve_index=False) + finally: + client.close() return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) @@ -536,7 +569,6 @@ def pull_all_from_table_or_query( RuntimeWarning, ) - connection_string = config.offline_store.connection_string db_name = config.offline_store.database collection = config.offline_store.collection feature_view_name = data_source.feature_view_name @@ -570,17 +602,21 @@ def pull_all_from_table_or_query( ] def _run() -> pyarrow.Table: - docs = _fetch_documents(connection_string, db_name, collection, pipeline) - if not docs: - return pyarrow.Table.from_pydict({}) - - df = pd.DataFrame(docs) - if not df.empty and "event_timestamp" in df.columns: - if df["event_timestamp"].dt.tz is None: - df["event_timestamp"] = pd.to_datetime( - df["event_timestamp"], utc=True - ) - return pyarrow.Table.from_pandas(df, preserve_index=False) + client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + try: + docs = _fetch_documents(client, db_name, collection, pipeline) + if not docs: + return pyarrow.Table.from_pydict({}) + + df = pd.DataFrame(docs) + if not df.empty and "event_timestamp" in df.columns: + if df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime( + df["event_timestamp"], utc=True + ) + return pyarrow.Table.from_pandas(df, preserve_index=False) + finally: + client.close() return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) @@ -604,7 +640,6 @@ def get_historical_features( RuntimeWarning, ) - connection_string = config.offline_store.connection_string db_name = config.offline_store.database feature_collection = config.offline_store.collection entity_key_version = config.entity_key_serialization_version @@ -662,7 +697,7 @@ def _run() -> pyarrow.Table: # Create temporary collection for query temp_collection_name = f"tmp_entity_df_{uuid.uuid4().hex[:12]}" - client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) + client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) try: db = client[db_name] temp_collection = db[temp_collection_name] From 91e939cc05b1028edd7fac829f271eec894597ec Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 18 Mar 2026 19:13:48 -0400 Subject: [PATCH 13/76] Add performance benchmarks comparing Ibis vs Native MongoDB offline stores Signed-off-by: Casey Clements --- .../benchmark_mongodb_offline_stores.py | 836 ++++++++++++++++++ 1 file changed, 836 insertions(+) create mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py b/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py new file mode 100644 index 00000000000..177023dd6fb --- /dev/null +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py @@ -0,0 +1,836 @@ +""" +Performance benchmarks comparing Ibis vs Native MongoDB offline store implementations. + +These tests measure performance across different scaling dimensions: +1. Row count scaling (entity_df size) +2. Feature width scaling (features per FeatureView) +3. Entity distribution (unique vs skewed/repeated entity_ids) + +Metrics captured: +- Runtime (wall clock) +- Memory (peak Python memory via tracemalloc) +- MongoDB server metrics (opcounters, execution stats) + +Run with: pytest benchmark_mongodb_offline_stores.py -v -s +Skip slow tests: pytest benchmark_mongodb_offline_stores.py -v -s -m "not slow" +""" + +import time +import tracemalloc +from dataclasses import dataclass, field +from datetime import datetime, timedelta +from typing import Any, Dict, Generator, Optional + +import pandas as pd +import pytest +import pytz + +pytest.importorskip("pymongo") + +from unittest.mock import MagicMock + +from pymongo import MongoClient +from testcontainers.mongodb import MongoDbContainer + +from feast import Entity, FeatureView, Field +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb import ( + MongoDBOfflineStoreIbis, + MongoDBOfflineStoreIbisConfig, +) +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( + MongoDBOfflineStoreNative, + MongoDBOfflineStoreNativeConfig, + MongoDBSourceNative, +) +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source import ( + MongoDBSource, +) +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import RepoConfig +from feast.types import Float64, Int64 +from feast.value_type import ValueType + +# Check if Docker is available +docker_available = False +try: + import docker + + try: + client = docker.from_env() + client.ping() + docker_available = True + except Exception: + pass +except ImportError: + pass + +_requires_docker = pytest.mark.skipif( + not docker_available, + reason="Docker is not available or not running.", +) + +ENTITY_KEY_VERSION = 3 + + +@dataclass +class BenchmarkResult: + """Container for benchmark results.""" + + implementation: str + test_name: str + dimension: str + value: int + duration_seconds: float + rows_per_second: float + peak_memory_mb: float = 0.0 + mongo_docs_examined: int = 0 + mongo_keys_examined: int = 0 + mongo_execution_time_ms: int = 0 + + +@dataclass +class MongoMetrics: + """MongoDB server metrics captured before/after a query.""" + + opcounters: Dict[str, int] = field(default_factory=dict) + docs_examined: int = 0 + keys_examined: int = 0 + + @staticmethod + def capture(client: Any) -> "MongoMetrics": + """Capture current MongoDB server metrics.""" + status = client.admin.command("serverStatus") + return MongoMetrics( + opcounters=dict(status.get("opcounters", {})), + ) + + def delta(self, after: "MongoMetrics") -> Dict[str, int]: + """Calculate delta between two metric snapshots.""" + return { + k: after.opcounters.get(k, 0) - self.opcounters.get(k, 0) + for k in after.opcounters + } + + +def _make_entity_id(driver_id: int) -> bytes: + """Create serialized entity key.""" + entity_key = EntityKeyProto() + entity_key.join_keys.append("driver_id") + val = ValueProto() + val.int64_val = driver_id + entity_key.entity_values.append(val) + return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) + + +@pytest.fixture(scope="module") +def mongodb_container() -> Generator[MongoDbContainer, None, None]: + """Start a MongoDB container for benchmarks.""" + container = MongoDbContainer( + "mongo:latest", + username="test", + password="test", # pragma: allowlist secret + ).with_exposed_ports(27017) + container.start() + yield container + container.stop() + + +@pytest.fixture +def mongodb_connection_string(mongodb_container: MongoDbContainer) -> str: + """Get MongoDB connection string.""" + exposed_port = mongodb_container.get_exposed_port(27017) + return f"mongodb://test:test@localhost:{exposed_port}" # pragma: allowlist secret + + +@pytest.fixture +def ibis_config(mongodb_connection_string: str) -> RepoConfig: + """RepoConfig for Ibis implementation.""" + return RepoConfig( + project="benchmark", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreIbisConfig( + connection_string=mongodb_connection_string, + database="benchmark_db", + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + + +@pytest.fixture +def native_config(mongodb_connection_string: str) -> RepoConfig: + """RepoConfig for Native implementation.""" + return RepoConfig( + project="benchmark", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreNativeConfig( + connection_string=mongodb_connection_string, + database="benchmark_db", + collection="feature_history", + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + + +def _generate_ibis_data( + client: MongoClient, + db_name: str, + collection_name: str, + num_entities: int, + num_features: int, + rows_per_entity: int = 5, +) -> datetime: + """Generate test data for Ibis (one collection per FV, flat schema).""" + collection = client[db_name][collection_name] + collection.drop() + + now = datetime.now(tz=pytz.UTC) + docs = [] + + for entity_id in range(num_entities): + for row in range(rows_per_entity): + doc = { + "driver_id": entity_id, + "event_timestamp": now - timedelta(hours=row), + } + for f in range(num_features): + doc[f"feature_{f}"] = float(entity_id * 100 + f + row * 0.1) + docs.append(doc) + + collection.insert_many(docs) + return now + + +def _generate_native_data( + client: MongoClient, + db_name: str, + collection_name: str, + feature_view_name: str, + num_entities: int, + num_features: int, + rows_per_entity: int = 5, +) -> datetime: + """Generate test data for Native (single collection, nested features).""" + collection = client[db_name][collection_name] + # Don't drop - may have multiple FVs in same collection + + now = datetime.now(tz=pytz.UTC) + docs = [] + + for entity_id in range(num_entities): + for row in range(rows_per_entity): + features = {} + for f in range(num_features): + features[f"feature_{f}"] = float(entity_id * 100 + f + row * 0.1) + + doc = { + "entity_id": _make_entity_id(entity_id), + "feature_view": feature_view_name, + "features": features, + "event_timestamp": now - timedelta(hours=row), + "created_at": now - timedelta(hours=row), + } + docs.append(doc) + + collection.insert_many(docs) + return now + + +def _create_ibis_fv(num_features: int) -> tuple: + """Create Ibis source and FeatureView.""" + source = MongoDBSource( + name="driver_benchmark", + database="benchmark_db", + collection="driver_benchmark", + timestamp_field="event_timestamp", + ) + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + + schema = [Field(name="driver_id", dtype=Int64)] + for f in range(num_features): + schema.append(Field(name=f"feature_{f}", dtype=Float64)) + + fv = FeatureView( + name="driver_benchmark", + entities=[entity], + schema=schema, + source=source, + ttl=timedelta(days=1), + ) + return source, fv + + +def _create_native_fv(num_features: int) -> tuple: + """Create Native source and FeatureView.""" + source = MongoDBSourceNative( + name="driver_benchmark", + timestamp_field="event_timestamp", + ) + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + + schema = [Field(name="driver_id", dtype=Int64)] + for f in range(num_features): + schema.append(Field(name=f"feature_{f}", dtype=Float64)) + + fv = FeatureView( + name="driver_benchmark", + entities=[entity], + schema=schema, + source=source, + ttl=timedelta(days=1), + ) + return source, fv + + +def _run_benchmark(func, name: str) -> float: + """Run a function and return elapsed time.""" + start = time.perf_counter() + func() # Execute the function + elapsed = time.perf_counter() - start + return elapsed + + +@dataclass +class FullBenchmarkResult: + """Full benchmark results with all metrics.""" + + elapsed_seconds: float + peak_memory_mb: float + mongo_opcounters_delta: Dict[str, int] + + +def _run_benchmark_full( + func, + mongo_client: Optional[Any] = None, +) -> FullBenchmarkResult: + """Run a benchmark capturing runtime, memory, and MongoDB metrics.""" + # Capture MongoDB metrics before + mongo_before = None + if mongo_client: + mongo_before = MongoMetrics.capture(mongo_client) + + # Start memory tracking + tracemalloc.start() + + # Run the benchmark + start = time.perf_counter() + func() + elapsed = time.perf_counter() - start + + # Capture peak memory + _, peak_memory = tracemalloc.get_traced_memory() + tracemalloc.stop() + peak_memory_mb = peak_memory / (1024 * 1024) + + # Capture MongoDB metrics after + mongo_delta = {} + if mongo_client and mongo_before: + mongo_after = MongoMetrics.capture(mongo_client) + mongo_delta = mongo_before.delta(mongo_after) + + return FullBenchmarkResult( + elapsed_seconds=elapsed, + peak_memory_mb=peak_memory_mb, + mongo_opcounters_delta=mongo_delta, + ) + + +def _print_benchmark_result( + impl: str, + dimension_name: str, + dimension_value: int, + result: FullBenchmarkResult, + num_rows: Optional[int] = None, +) -> None: + """Pretty print benchmark results.""" + print(f"\n[{impl}] {dimension_name}: {dimension_value:,}") + print(f" Time: {result.elapsed_seconds:.3f}s") + print(f" Memory: {result.peak_memory_mb:.1f} MB") + if num_rows: + rate = num_rows / result.elapsed_seconds if result.elapsed_seconds > 0 else 0 + print(f" Rate: {rate:,.0f} rows/s") + if result.mongo_opcounters_delta: + print(f" Mongo ops: {result.mongo_opcounters_delta}") + + +# ============================================================================= +# Test 1: Scale Rows (entity_df size) +# ============================================================================= + +ROW_COUNTS = [ + 1000, + 5000, + 10000, +] # Reduced for CI; use [10000, 50000, 100000, 500000] for full benchmark + + +@_requires_docker +@pytest.mark.parametrize("num_rows", ROW_COUNTS) +def test_scale_rows_ibis( + mongodb_connection_string: str, ibis_config: RepoConfig, num_rows: int +) -> None: + """Benchmark Ibis implementation with varying entity_df sizes. + + Measures: runtime, peak memory, MongoDB opcounters. + """ + num_features = 10 + num_entities = num_rows # One row per entity for simplicity + + client = MongoClient(mongodb_connection_string) + try: + now = _generate_ibis_data( + client, + "benchmark_db", + "driver_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=3, + ) + + _, fv = _create_ibis_fv(num_features) + + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] + + def run_query(): + job = MongoDBOfflineStoreIbis.get_historical_features( + config=ibis_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + _print_benchmark_result("IBIS", "Rows", num_rows, result, num_rows=num_rows) + + finally: + client.close() + + +@_requires_docker +@pytest.mark.parametrize("num_rows", ROW_COUNTS) +def test_scale_rows_native( + mongodb_connection_string: str, native_config: RepoConfig, num_rows: int +) -> None: + """Benchmark Native implementation with varying entity_df sizes. + + Measures: runtime, peak memory, MongoDB opcounters. + """ + num_features = 10 + num_entities = num_rows + + client = MongoClient(mongodb_connection_string) + try: + client["benchmark_db"]["feature_history"].drop() + now = _generate_native_data( + client, + "benchmark_db", + "feature_history", + "driver_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=3, + ) + + _, fv = _create_native_fv(num_features) + + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] + + def run_query(): + job = MongoDBOfflineStoreNative.get_historical_features( + config=native_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + _print_benchmark_result("NATIVE", "Rows", num_rows, result, num_rows=num_rows) + + finally: + client.close() + + +# ============================================================================= +# Test 2: Wide Feature Views (features per FV) +# ============================================================================= + +FEATURE_COUNTS = [10, 50, 100] # Use [50, 100, 150, 200] for full benchmark + + +@_requires_docker +@pytest.mark.parametrize("num_features", FEATURE_COUNTS) +def test_wide_features_ibis( + mongodb_connection_string: str, ibis_config: RepoConfig, num_features: int +) -> None: + """Benchmark Ibis with varying feature width.""" + num_entities = 1000 + + client = MongoClient(mongodb_connection_string) + try: + now = _generate_ibis_data( + client, + "benchmark_db", + "driver_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=3, + ) + + _, fv = _create_ibis_fv(num_features) + + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] + + def run_query(): + job = MongoDBOfflineStoreIbis.get_historical_features( + config=ibis_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + _print_benchmark_result( + "IBIS", "Features", num_features, result, num_rows=num_entities + ) + + finally: + client.close() + + +@_requires_docker +@pytest.mark.parametrize("num_features", FEATURE_COUNTS) +def test_wide_features_native( + mongodb_connection_string: str, native_config: RepoConfig, num_features: int +) -> None: + """Benchmark Native with varying feature width.""" + num_entities = 1000 + + client = MongoClient(mongodb_connection_string) + try: + client["benchmark_db"]["feature_history"].drop() + now = _generate_native_data( + client, + "benchmark_db", + "feature_history", + "driver_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=3, + ) + + _, fv = _create_native_fv(num_features) + + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] + + def run_query(): + job = MongoDBOfflineStoreNative.get_historical_features( + config=native_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + _print_benchmark_result( + "NATIVE", "Features", num_features, result, num_rows=num_entities + ) + + finally: + client.close() + + +# ============================================================================= +# Test 3: Skewed Entity Distribution +# ============================================================================= + + +@_requires_docker +@pytest.mark.parametrize("unique_ratio", [1.0, 0.5, 0.1]) # 100%, 50%, 10% unique +def test_entity_skew_ibis( + mongodb_connection_string: str, ibis_config: RepoConfig, unique_ratio: float +) -> None: + """Benchmark Ibis with varying entity uniqueness in entity_df.""" + import numpy as np + + total_rows = 5000 + num_features = 10 + num_unique_entities = int(total_rows * unique_ratio) + num_unique_entities = max(num_unique_entities, 1) + + client = MongoClient(mongodb_connection_string) + try: + now = _generate_ibis_data( + client, + "benchmark_db", + "driver_benchmark", + num_entities=num_unique_entities, + num_features=num_features, + rows_per_entity=5, + ) + + _, fv = _create_ibis_fv(num_features) + + # Create entity_df with repeated entity_ids + entity_ids = np.random.choice( + num_unique_entities, size=total_rows, replace=True + ) + entity_df = pd.DataFrame( + { + "driver_id": entity_ids, + "event_timestamp": [ + now - timedelta(minutes=i % 60) for i in range(total_rows) + ], + } + ) + + feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] + + def run_query(): + job = MongoDBOfflineStoreIbis.get_historical_features( + config=ibis_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + print( + f"\n[IBIS] Unique ratio: {unique_ratio:.0%} ({num_unique_entities:,} unique / {total_rows:,} rows)" + ) + print(f" Time: {result.elapsed_seconds:.3f}s") + print(f" Memory: {result.peak_memory_mb:.1f} MB") + print(f" Mongo ops: {result.mongo_opcounters_delta}") + + finally: + client.close() + + +@_requires_docker +@pytest.mark.parametrize("unique_ratio", [1.0, 0.5, 0.1]) +def test_entity_skew_native( + mongodb_connection_string: str, native_config: RepoConfig, unique_ratio: float +) -> None: + """Benchmark Native with varying entity uniqueness in entity_df.""" + import numpy as np + + total_rows = 5000 + num_features = 10 + num_unique_entities = int(total_rows * unique_ratio) + num_unique_entities = max(num_unique_entities, 1) + + client = MongoClient(mongodb_connection_string) + try: + client["benchmark_db"]["feature_history"].drop() + now = _generate_native_data( + client, + "benchmark_db", + "feature_history", + "driver_benchmark", + num_entities=num_unique_entities, + num_features=num_features, + rows_per_entity=5, + ) + + _, fv = _create_native_fv(num_features) + + entity_ids = np.random.choice( + num_unique_entities, size=total_rows, replace=True + ) + entity_df = pd.DataFrame( + { + "driver_id": entity_ids, + "event_timestamp": [ + now - timedelta(minutes=i % 60) for i in range(total_rows) + ], + } + ) + + feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] + + def run_query(): + job = MongoDBOfflineStoreNative.get_historical_features( + config=native_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + print( + f"\n[NATIVE] Unique ratio: {unique_ratio:.0%} ({num_unique_entities:,} unique / {total_rows:,} rows)" + ) + print(f" Time: {result.elapsed_seconds:.3f}s") + print(f" Memory: {result.peak_memory_mb:.1f} MB") + print(f" Mongo ops: {result.mongo_opcounters_delta}") + + finally: + client.close() + + +# ============================================================================= +# Summary comparison test +# ============================================================================= + + +@_requires_docker +def test_summary_comparison( + mongodb_connection_string: str, ibis_config: RepoConfig, native_config: RepoConfig +) -> None: + """Run a standard comparison and print summary with full metrics.""" + num_entities = 2000 + num_features = 20 + + client = MongoClient(mongodb_connection_string) + try: + # Setup Ibis data + now = _generate_ibis_data( + client, + "benchmark_db", + "driver_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=5, + ) + + # Setup Native data + client["benchmark_db"]["feature_history"].drop() + _generate_native_data( + client, + "benchmark_db", + "feature_history", + "driver_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=5, + ) + + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] + + # Ibis benchmark + _, ibis_fv = _create_ibis_fv(num_features) + + def run_ibis(): + job = MongoDBOfflineStoreIbis.get_historical_features( + config=ibis_config, + feature_views=[ibis_fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + ibis_result = _run_benchmark_full(run_ibis, mongo_client=client) + + # Native benchmark + _, native_fv = _create_native_fv(num_features) + + def run_native(): + job = MongoDBOfflineStoreNative.get_historical_features( + config=native_config, + feature_views=[native_fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + native_result = _run_benchmark_full(run_native, mongo_client=client) + + # Print summary + print("\n" + "=" * 70) + print("SUMMARY COMPARISON") + print("=" * 70) + print(f"Entities: {num_entities:,} | Features: {num_features}") + print("-" * 70) + print(f"{'Metric':<20} {'Ibis':>20} {'Native':>20}") + print("-" * 70) + print( + f"{'Time (s)':<20} {ibis_result.elapsed_seconds:>20.3f} {native_result.elapsed_seconds:>20.3f}" + ) + print( + f"{'Memory (MB)':<20} {ibis_result.peak_memory_mb:>20.1f} {native_result.peak_memory_mb:>20.1f}" + ) + print( + f"{'Rows/sec':<20} {num_entities / ibis_result.elapsed_seconds:>20,.0f} {num_entities / native_result.elapsed_seconds:>20,.0f}" + ) + print("-" * 70) + + if native_result.elapsed_seconds > 0: + ratio = native_result.elapsed_seconds / ibis_result.elapsed_seconds + print(f"Ibis is {ratio:.1f}x faster than Native") + print("=" * 70) + + finally: + client.close() From e4bfc31bef8bcacc8dfbe2c50e46e514293f8329 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 18 Mar 2026 19:28:00 -0400 Subject: [PATCH 14/76] Refactor Native get_historical_features: replace with fetch+pandas join MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Eliminate -based PIT join which scaled poorly (O(n×m)) - Use single query to fetch all matching feature data - Batch entity_ids into chunks of 1000 for large queries - Flatten features subdoc with pd.json_normalize - Apply pd.merge_asof for efficient PIT join per FeatureView - Handle TTL filtering in pandas instead of MQL \ - Remove unused _ttl_to_ms and _build_ttl_gte_expr helpers Performance improvement: - Before: 10k rows in ~188s (53 rows/s) - After: 10k rows in ~7.4s (1,354 rows/s) - Now competitive with Ibis implementation Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 273 ++++++++---------- 1 file changed, 124 insertions(+), 149 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py index c9cbae587a2..6e1f610d377 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -81,7 +81,6 @@ """ import json -import uuid import warnings from datetime import datetime, timezone from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union @@ -375,55 +374,6 @@ def _serialize_entity_key_from_row( return serialize_entity_key(entity_key, entity_key_serialization_version) -def _ttl_to_ms(fv: FeatureView) -> Optional[int]: - """Convert FeatureView TTL to milliseconds.""" - if fv.ttl is None: - return None - return int(fv.ttl.total_seconds() * 1000) - - -def _build_ttl_gte_expr(feature_views: List[FeatureView]) -> Optional[Dict[str, Any]]: - """Build a $gte expression with per-FV TTL using $switch. - - Returns a MongoDB expression that evaluates to: - event_timestamp >= (entity_timestamp - ttl_for_this_feature_view) - - Each feature_view can have a different TTL, handled via $switch branches. - If no feature views have TTL, returns None (no filtering needed). - """ - branches = [] - - for fv in feature_views: - ttl_ms = _ttl_to_ms(fv) - if ttl_ms is None: - # No TTL for this FV - skip (effectively infinite history) - continue - - branches.append( - { - "case": {"$eq": ["$feature_view", fv.name]}, - "then": {"$subtract": ["$$ts", ttl_ms]}, - } - ) - - # If no TTLs at all, no lower bound needed - if not branches: - return None - - return { - "$gte": [ - "$event_timestamp", - { - "$switch": { - "branches": branches, - # Default: no lower bound (for FVs without TTL) - "default": {"$literal": 0}, - } - }, - ] - } - - class MongoDBOfflineStoreNative(OfflineStore): """Native MongoDB offline store using single-collection schema. @@ -630,6 +580,13 @@ def get_historical_features( project: str, full_feature_names: bool = False, ) -> RetrievalJob: + """Fetch historical features using a "fetch + pandas join" strategy. + + Instead of using $lookup (which scales poorly), this: + 1. Extracts unique entity_ids and computes timestamp bounds + 2. Fetches all matching feature data in one query + 3. Uses pd.merge_asof for efficient point-in-time joins in Python + """ if isinstance(entity_df, str): raise ValueError( "MongoDBOfflineStoreNative does not support SQL entity_df strings. " @@ -654,18 +611,10 @@ def get_historical_features( fv_to_features.setdefault(fv_name, []).append(feat_name) fv_names = list(fv_to_features.keys()) - - # Build per-FV TTL expression using $switch - relevant_fvs = [fv for fv in feature_views if fv.name in fv_to_features] - ttl_expr = _build_ttl_gte_expr(relevant_fvs) + fv_by_name = {fv.name: fv for fv in feature_views} def _run() -> pyarrow.Table: - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - - # Prepare entity_df: ensure timestamps are UTC and serialize entity keys + # Prepare entity_df: ensure timestamps are UTC result = entity_df.copy() if result[event_timestamp_col].dt.tz is None: result[event_timestamp_col] = pd.to_datetime( @@ -683,110 +632,136 @@ def _run() -> pyarrow.Table: axis=1, ) - # Build temp collection documents - temp_docs = [] - for _, row in result.iterrows(): - temp_docs.append( - { - "entity_id": row["_entity_id"], - "event_timestamp": row[event_timestamp_col], - "_row_idx": _, # Preserve original order - } - ) + # Extract unique entity_ids and timestamp bounds + unique_entity_ids = result["_entity_id"].unique().tolist() + max_ts = result[event_timestamp_col].max() - # Create temporary collection for query - temp_collection_name = f"tmp_entity_df_{uuid.uuid4().hex[:12]}" + # Batch entity_ids into chunks to avoid huge $in queries + BATCH_SIZE = 1000 + all_feature_docs: List[Dict] = [] client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) try: - db = client[db_name] - temp_collection = db[temp_collection_name] - temp_collection.insert_many(temp_docs) - - # Build $lookup subpipeline with PIT join logic - # Match: entity_id, feature_view in list, event_timestamp <= entity.ts - match_conditions: List[Dict[str, Any]] = [ - {"$eq": ["$entity_id", "$$entity_id"]}, - {"$in": ["$feature_view", fv_names]}, - {"$lte": ["$event_timestamp", "$$ts"]}, - ] - # Add per-FV TTL filter using $switch - if ttl_expr is not None: - match_conditions.append(ttl_expr) - - lookup_pipeline: List[Dict[str, Any]] = [ - {"$match": {"$expr": {"$and": match_conditions}}}, - {"$sort": {"feature_view": 1, "event_timestamp": -1}}, - { - "$group": { - "_id": "$feature_view", - "doc": {"$first": "$$ROOT"}, - } - }, - ] + coll = client[db_name][feature_collection] - # Main aggregation pipeline - pipeline: List[Dict[str, Any]] = [ - { - "$lookup": { - "from": feature_collection, - "let": { - "entity_id": "$entity_id", - "ts": "$event_timestamp", - }, - "pipeline": lookup_pipeline, - "as": "feature_rows", - } - }, - {"$sort": {"_row_idx": 1}}, # Preserve original order - ] + for i in range(0, len(unique_entity_ids), BATCH_SIZE): + batch_ids = unique_entity_ids[i : i + BATCH_SIZE] - docs = list(temp_collection.aggregate(pipeline)) + # Single query: fetch all matching feature data + query = { + "entity_id": {"$in": batch_ids}, + "feature_view": {"$in": fv_names}, + "event_timestamp": {"$lte": max_ts}, + } + docs = list(coll.find(query, {"_id": 0})) + all_feature_docs.extend(docs) finally: - # Cleanup temp collection - client[db_name][temp_collection_name].drop() client.close() - if not docs: - return pyarrow.Table.from_pydict({}) - - # Build result DataFrame - result = result.reset_index(drop=True) - rows = [] - for doc in docs: - # Start with entity columns from original entity_df - row_idx = doc["_row_idx"] - row = result.iloc[row_idx][ - entity_columns + [event_timestamp_col] - ].to_dict() - - # Extract features from each feature_view's matched doc - feature_rows_by_fv = { - fr["_id"]: fr["doc"] for fr in doc.get("feature_rows", []) - } - - # Extract features from each feature_view's matched doc - # TTL is already applied server-side via $switch expression + # Handle empty result + if not all_feature_docs: + # Return entity_df with NULL feature columns for fv_name, features in fv_to_features.items(): - fv_doc = feature_rows_by_fv.get(fv_name) + for feat in features: + col_name = f"{fv_name}__{feat}" if full_feature_names else feat + result[col_name] = None + result = result.drop(columns=["_entity_id"]) + return pyarrow.Table.from_pandas(result, preserve_index=False) + + # Convert to DataFrame and flatten features subdoc + feature_df = pd.DataFrame(all_feature_docs) + # Rename entity_id to _entity_id to match result DataFrame + feature_df = feature_df.rename(columns={"entity_id": "_entity_id"}) + + # Flatten nested 'features' dict into top-level columns + if "features" in feature_df.columns: + features_expanded = pd.json_normalize(feature_df["features"]) + feature_df = pd.concat( + [feature_df.drop(columns=["features"]), features_expanded], axis=1 + ) + + # Ensure timestamps are tz-aware + if feature_df["event_timestamp"].dt.tz is None: + feature_df["event_timestamp"] = pd.to_datetime( + feature_df["event_timestamp"], utc=True + ) + + # Split by feature_view and perform PIT join for each + result = result.sort_values(event_timestamp_col).reset_index(drop=True) + + for fv_name, features in fv_to_features.items(): + fv = fv_by_name.get(fv_name) + + # Filter to this feature_view's data + fv_df = feature_df[feature_df["feature_view"] == fv_name].copy() + + if fv_df.empty: + # No data for this FV - fill with NULLs for feat in features: col_name = f"{fv_name}__{feat}" if full_feature_names else feat - if fv_doc is None: - row[col_name] = None - else: - row[col_name] = fv_doc.get("features", {}).get(feat) - - rows.append(row) - - result_df = pd.DataFrame(rows) - if not result_df.empty and event_timestamp_col in result_df.columns: - if result_df[event_timestamp_col].dt.tz is None: - result_df[event_timestamp_col] = pd.to_datetime( - result_df[event_timestamp_col], utc=True + result[col_name] = None + continue + + # Sort by timestamp for merge_asof + fv_df = fv_df.sort_values("event_timestamp").reset_index(drop=True) + + # Select columns for merge + merge_cols = ["_entity_id", "event_timestamp"] + [ + f for f in features if f in fv_df.columns + ] + fv_df_subset = fv_df[ + [c for c in merge_cols if c in fv_df.columns] + ].copy() + + # Rename to avoid conflicts + fv_df_subset = fv_df_subset.rename( + columns={"event_timestamp": "_fv_ts"} + ) + + # Point-in-time join using merge_asof + result = pd.merge_asof( + result, + fv_df_subset, + left_on=event_timestamp_col, + right_on="_fv_ts", + by="_entity_id", + direction="backward", + ) + + # Apply TTL: null out stale features + if fv and fv.ttl: + cutoff = result[event_timestamp_col] - fv.ttl + stale_mask = result["_fv_ts"] < cutoff + for feat in features: + if feat in result.columns: + result.loc[stale_mask, feat] = None + + # Rename features if full_feature_names + for feat in features: + if feat in result.columns and full_feature_names: + result = result.rename(columns={feat: f"{fv_name}__{feat}"}) + elif feat not in result.columns: + # Feature wasn't in the data - add NULL column + col_name = f"{fv_name}__{feat}" if full_feature_names else feat + result[col_name] = None + + # Drop temporary column + result = result.drop(columns=["_fv_ts"], errors="ignore") + + # Remove internal entity_id column and restore original order + result = result.drop(columns=["_entity_id"], errors="ignore") + result = result.sort_index().reset_index(drop=True) + + # Ensure timestamp column is still tz-aware + if not result.empty and event_timestamp_col in result.columns: + if result[event_timestamp_col].dt.tz is None: + result[event_timestamp_col] = pd.to_datetime( + result[event_timestamp_col], utc=True ) - return pyarrow.Table.from_pandas(result_df, preserve_index=False) + + return pyarrow.Table.from_pandas(result, preserve_index=False) return MongoDBNativeRetrievalJob( query_fn=_run, From 6218fa8448465bd952a2ef5618e919cb4d1b43ca Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 19 Mar 2026 07:39:30 -0400 Subject: [PATCH 15/76] Refactor get_historical_features with chunked processing for large entity_df - Add CHUNK_SIZE (5000) for entity_df processing to bound memory usage - Extract _run_single helper function for processing each chunk - Add _chunk_dataframe generator for yielding DataFrame slices - Preserve original row ordering via _row_idx column - Exclude internal columns (prefixed with _) from entity key serialization - Concat chunk results and restore ordering at the end This allows processing arbitrarily large entity_df while keeping memory bounded by processing in 5000-row chunks. Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 110 +++++++++++------- 1 file changed, 70 insertions(+), 40 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py index 6e1f610d377..8c7822bca44 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -83,7 +83,17 @@ import json import warnings from datetime import datetime, timezone -from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union +from typing import ( + Any, + Callable, + Dict, + Generator, + Iterable, + List, + Optional, + Tuple, + Union, +) import pandas as pd import pyarrow @@ -584,8 +594,10 @@ def get_historical_features( Instead of using $lookup (which scales poorly), this: 1. Extracts unique entity_ids and computes timestamp bounds - 2. Fetches all matching feature data in one query + 2. Fetches all matching feature data in batched queries 3. Uses pd.merge_asof for efficient point-in-time joins in Python + + For large entity_df, processing is chunked to bound memory usage. """ if isinstance(entity_df, str): raise ValueError( @@ -613,16 +625,33 @@ def get_historical_features( fv_names = list(fv_to_features.keys()) fv_by_name = {fv.name: fv for fv in feature_views} - def _run() -> pyarrow.Table: + # Chunk size for entity_df processing (bounds memory usage) + CHUNK_SIZE = 5000 + # Batch size for MongoDB $in queries + MONGO_BATCH_SIZE = 1000 + + def _chunk_dataframe( + df: pd.DataFrame, size: int + ) -> Generator[pd.DataFrame, None, None]: + """Yield successive chunks of a DataFrame.""" + for i in range(0, len(df), size): + yield df.iloc[i : i + size] + + def _run_single(entity_subset_df: pd.DataFrame) -> pd.DataFrame: + """Process a single chunk of entity_df and return joined features.""" # Prepare entity_df: ensure timestamps are UTC - result = entity_df.copy() + result = entity_subset_df.copy() if result[event_timestamp_col].dt.tz is None: result[event_timestamp_col] = pd.to_datetime( result[event_timestamp_col], utc=True ) - # Get join keys (all columns except event_timestamp) - entity_columns = [c for c in result.columns if c != event_timestamp_col] + # Get join keys (all columns except event_timestamp and internal columns) + entity_columns = [ + c + for c in result.columns + if c != event_timestamp_col and not c.startswith("_") + ] # Serialize entity keys to bytes (same format as online store) result["_entity_id"] = result.apply( @@ -632,22 +661,20 @@ def _run() -> pyarrow.Table: axis=1, ) - # Extract unique entity_ids and timestamp bounds + # Extract unique entity_ids and timestamp bounds for this chunk unique_entity_ids = result["_entity_id"].unique().tolist() max_ts = result[event_timestamp_col].max() - # Batch entity_ids into chunks to avoid huge $in queries - BATCH_SIZE = 1000 + # Fetch feature data in batches all_feature_docs: List[Dict] = [] client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) try: coll = client[db_name][feature_collection] - for i in range(0, len(unique_entity_ids), BATCH_SIZE): - batch_ids = unique_entity_ids[i : i + BATCH_SIZE] + for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): + batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] - # Single query: fetch all matching feature data query = { "entity_id": {"$in": batch_ids}, "feature_view": {"$in": fv_names}, @@ -661,66 +688,53 @@ def _run() -> pyarrow.Table: # Handle empty result if not all_feature_docs: - # Return entity_df with NULL feature columns for fv_name, features in fv_to_features.items(): for feat in features: col_name = f"{fv_name}__{feat}" if full_feature_names else feat result[col_name] = None - result = result.drop(columns=["_entity_id"]) - return pyarrow.Table.from_pandas(result, preserve_index=False) + return result.drop(columns=["_entity_id"]) # Convert to DataFrame and flatten features subdoc feature_df = pd.DataFrame(all_feature_docs) - - # Rename entity_id to _entity_id to match result DataFrame feature_df = feature_df.rename(columns={"entity_id": "_entity_id"}) - # Flatten nested 'features' dict into top-level columns if "features" in feature_df.columns: features_expanded = pd.json_normalize(feature_df["features"]) feature_df = pd.concat( [feature_df.drop(columns=["features"]), features_expanded], axis=1 ) - # Ensure timestamps are tz-aware if feature_df["event_timestamp"].dt.tz is None: feature_df["event_timestamp"] = pd.to_datetime( feature_df["event_timestamp"], utc=True ) - # Split by feature_view and perform PIT join for each + # Sort result for merge_asof result = result.sort_values(event_timestamp_col).reset_index(drop=True) + # Perform PIT join for each feature view for fv_name, features in fv_to_features.items(): fv = fv_by_name.get(fv_name) - - # Filter to this feature_view's data fv_df = feature_df[feature_df["feature_view"] == fv_name].copy() if fv_df.empty: - # No data for this FV - fill with NULLs for feat in features: col_name = f"{fv_name}__{feat}" if full_feature_names else feat result[col_name] = None continue - # Sort by timestamp for merge_asof fv_df = fv_df.sort_values("event_timestamp").reset_index(drop=True) - # Select columns for merge merge_cols = ["_entity_id", "event_timestamp"] + [ f for f in features if f in fv_df.columns ] fv_df_subset = fv_df[ [c for c in merge_cols if c in fv_df.columns] ].copy() - - # Rename to avoid conflicts fv_df_subset = fv_df_subset.rename( columns={"event_timestamp": "_fv_ts"} ) - # Point-in-time join using merge_asof result = pd.merge_asof( result, fv_df_subset, @@ -730,7 +744,7 @@ def _run() -> pyarrow.Table: direction="backward", ) - # Apply TTL: null out stale features + # Apply TTL if fv and fv.ttl: cutoff = result[event_timestamp_col] - fv.ttl stale_mask = result["_fv_ts"] < cutoff @@ -743,25 +757,41 @@ def _run() -> pyarrow.Table: if feat in result.columns and full_feature_names: result = result.rename(columns={feat: f"{fv_name}__{feat}"}) elif feat not in result.columns: - # Feature wasn't in the data - add NULL column col_name = f"{fv_name}__{feat}" if full_feature_names else feat result[col_name] = None - # Drop temporary column result = result.drop(columns=["_fv_ts"], errors="ignore") - # Remove internal entity_id column and restore original order - result = result.drop(columns=["_entity_id"], errors="ignore") - result = result.sort_index().reset_index(drop=True) + return result.drop(columns=["_entity_id"], errors="ignore") - # Ensure timestamp column is still tz-aware - if not result.empty and event_timestamp_col in result.columns: - if result[event_timestamp_col].dt.tz is None: - result[event_timestamp_col] = pd.to_datetime( - result[event_timestamp_col], utc=True + def _run() -> pyarrow.Table: + # Add row index to preserve original ordering + working_df = entity_df.copy() + working_df["_row_idx"] = range(len(working_df)) + + if len(working_df) <= CHUNK_SIZE: + # Small workload: process in single pass + result_df = _run_single(working_df) + else: + # Large workload: process in chunks + chunk_results = [] + for chunk in _chunk_dataframe(working_df, CHUNK_SIZE): + chunk_results.append(_run_single(chunk)) + + result_df = pd.concat(chunk_results, ignore_index=True) + + # Restore original ordering and remove index column + result_df = result_df.sort_values("_row_idx").reset_index(drop=True) + result_df = result_df.drop(columns=["_row_idx"], errors="ignore") + + # Ensure timestamp column is tz-aware + if not result_df.empty and event_timestamp_col in result_df.columns: + if result_df[event_timestamp_col].dt.tz is None: + result_df[event_timestamp_col] = pd.to_datetime( + result_df[event_timestamp_col], utc=True ) - return pyarrow.Table.from_pandas(result, preserve_index=False) + return pyarrow.Table.from_pandas(result_df, preserve_index=False) return MongoDBNativeRetrievalJob( query_fn=_run, From b7ffd844c4e8d2849976fdba340d26c969b4eb11 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 19 Mar 2026 09:58:50 -0400 Subject: [PATCH 16/76] Optimize Native get_historical_features: reuse client, increase batch sizes Performance optimizations: - Reuse MongoClient across chunks (was creating new client per chunk) - Increase CHUNK_SIZE from 5,000 to 50,000 rows - Increase MONGO_BATCH_SIZE from 1,000 to 10,000 entity_ids - Pass collection to _run_single instead of creating client each time - Make index creation idempotent (check for existing index) Results (100k rows): - Before: 21.7s - After: 5.2s (4.2x faster) Results (1M rows): - Before: 1664s (28 min) - After: 212s (3.5 min) (7.8x faster) Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 84 +++++++++++-------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py index 8c7822bca44..aa0c88f0331 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -400,15 +400,24 @@ class MongoDBOfflineStoreNative(OfflineStore): @staticmethod def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: - """Create recommended indexes on the feature_history collection.""" + """Create recommended indexes on the feature_history collection. + + Uses create_index with background=True. If index already exists + (with same or different name), this is a no-op. + """ collection = client[db_name][collection_name] + # Check if an equivalent index already exists + existing_indexes = collection.index_information() + target_key = [("entity_id", 1), ("feature_view", 1), ("event_timestamp", -1)] + + for idx_info in existing_indexes.values(): + if idx_info.get("key") == target_key: + return # Index already exists + collection.create_index( - [ - ("entity_id", 1), - ("feature_view", 1), - ("event_timestamp", -1), - ], + target_key, name="entity_fv_ts_idx", + background=True, ) @classmethod @@ -626,9 +635,9 @@ def get_historical_features( fv_by_name = {fv.name: fv for fv in feature_views} # Chunk size for entity_df processing (bounds memory usage) - CHUNK_SIZE = 5000 + CHUNK_SIZE = 50_000 # Batch size for MongoDB $in queries - MONGO_BATCH_SIZE = 1000 + MONGO_BATCH_SIZE = 10_000 def _chunk_dataframe( df: pd.DataFrame, size: int @@ -637,8 +646,13 @@ def _chunk_dataframe( for i in range(0, len(df), size): yield df.iloc[i : i + size] - def _run_single(entity_subset_df: pd.DataFrame) -> pd.DataFrame: - """Process a single chunk of entity_df and return joined features.""" + def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: + """Process a single chunk of entity_df and return joined features. + + Args: + entity_subset_df: Chunk of entity DataFrame to process + coll: MongoDB collection object (reused across chunks) + """ # Prepare entity_df: ensure timestamps are UTC result = entity_subset_df.copy() if result[event_timestamp_col].dt.tz is None: @@ -668,23 +682,16 @@ def _run_single(entity_subset_df: pd.DataFrame) -> pd.DataFrame: # Fetch feature data in batches all_feature_docs: List[Dict] = [] - client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) - try: - coll = client[db_name][feature_collection] - - for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): - batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] - - query = { - "entity_id": {"$in": batch_ids}, - "feature_view": {"$in": fv_names}, - "event_timestamp": {"$lte": max_ts}, - } - docs = list(coll.find(query, {"_id": 0})) - all_feature_docs.extend(docs) + for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): + batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] - finally: - client.close() + query = { + "entity_id": {"$in": batch_ids}, + "feature_view": {"$in": fv_names}, + "event_timestamp": {"$lte": max_ts}, + } + docs = list(coll.find(query, {"_id": 0})) + all_feature_docs.extend(docs) # Handle empty result if not all_feature_docs: @@ -769,16 +776,23 @@ def _run() -> pyarrow.Table: working_df = entity_df.copy() working_df["_row_idx"] = range(len(working_df)) - if len(working_df) <= CHUNK_SIZE: - # Small workload: process in single pass - result_df = _run_single(working_df) - else: - # Large workload: process in chunks - chunk_results = [] - for chunk in _chunk_dataframe(working_df, CHUNK_SIZE): - chunk_results.append(_run_single(chunk)) + # Create client once for all chunks + client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + try: + coll = client[db_name][feature_collection] + + if len(working_df) <= CHUNK_SIZE: + # Small workload: process in single pass + result_df = _run_single(working_df, coll) + else: + # Large workload: process in chunks + chunk_results = [] + for chunk in _chunk_dataframe(working_df, CHUNK_SIZE): + chunk_results.append(_run_single(chunk, coll)) - result_df = pd.concat(chunk_results, ignore_index=True) + result_df = pd.concat(chunk_results, ignore_index=True) + finally: + client.close() # Restore original ordering and remove index column result_df = result_df.sort_values("_row_idx").reset_index(drop=True) From affdc2d401551443ec1c8dc467cbeaf11c2bef61 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 19 Mar 2026 13:46:30 -0400 Subject: [PATCH 17/76] Remove duplicate MongoDBOfflineStoreNative from mongodb.py The Native implementation now lives exclusively in mongodb_native.py with the single-collection schema. This removes the confusing duplicate that used the Ibis collection-per-FV schema. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 416 +----------------- 1 file changed, 2 insertions(+), 414 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 51100ef8270..10ffd6c5333 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -13,12 +13,11 @@ # limitations under the License. import warnings -from datetime import datetime, timezone -from typing import Any, Callable, Dict, List, Optional, Union +from datetime import datetime +from typing import Any, Callable, List, Optional, Union import ibis import pandas as pd -import pyarrow from ibis.expr.types import Table from pydantic import StrictStr @@ -45,14 +44,9 @@ from feast.infra.offline_stores.offline_store import ( OfflineStore, RetrievalJob, - RetrievalMetadata, -) -from feast.infra.offline_stores.offline_utils import ( - infer_event_timestamp_from_entity_df, ) from feast.infra.registry.base_registry import BaseRegistry from feast.repo_config import FeastConfigBaseModel, RepoConfig -from feast.saved_dataset import SavedDatasetStorage class MongoDBOfflineStoreIbisConfig(FeastConfigBaseModel): @@ -247,409 +241,3 @@ def writer( client.close() return writer - - -# --------------------------------------------------------------------------- -# Native MQL implementation -# --------------------------------------------------------------------------- - - -class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): - """Configuration for the MongoDB native-MQL offline store.""" - - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStoreNative" - """Offline store type selector""" - - connection_string: StrictStr = "mongodb://localhost:27017" - """MongoDB connection URI""" - - database: StrictStr = "feast" - """Default MongoDB database name""" - - -def _fetch_collection_as_arrow( - connection_string: str, - db_name: str, - collection: str, - pipeline: Optional[List[Dict]] = None, -) -> pyarrow.Table: - """Run an aggregation pipeline (or full scan) via PyMongo and return a pyarrow Table. - - If *pipeline* is None the entire collection is scanned (``_id`` excluded). - The ``_id`` field is stripped from every result document before conversion. - """ - if MongoClient is None: - raise FeastExtrasDependencyImportError("mongodb", "pymongo is not installed.") - client: Any = MongoClient(connection_string, driver=DRIVER_METADATA, tz_aware=True) - try: - if pipeline is not None: - docs = list(client[db_name][collection].aggregate(pipeline)) - else: - docs = list(client[db_name][collection].find({}, {"_id": 0})) - finally: - client.close() - - if not docs: - return pyarrow.table({}) - - for doc in docs: - doc.pop("_id", None) - - return pyarrow.Table.from_pylist(docs) - - -class MongoDBNativeRetrievalJob(RetrievalJob): - """A RetrievalJob whose results come from a lazy PyMongo query callable. - - The callable is only executed when the caller materialises the job (e.g. - ``to_df()``, ``to_arrow()``, ``persist()``). - """ - - def __init__( - self, - query_fn: Callable[[], pyarrow.Table], - full_feature_names: bool, - on_demand_feature_views: List, - metadata: Optional[RetrievalMetadata], - config: RepoConfig, - ) -> None: - super().__init__() - self._query_fn = query_fn - self._full_feature_names = full_feature_names - self._on_demand_feature_views = on_demand_feature_views or [] - self._metadata = metadata - self._config = config - - def _to_arrow_internal(self, timeout: Optional[int] = None) -> pyarrow.Table: - return self._query_fn() - - def _to_df_internal(self, timeout: Optional[int] = None) -> pd.DataFrame: - return self._to_arrow_internal().to_pandas() - - @property - def full_feature_names(self) -> bool: - return self._full_feature_names - - @property - def on_demand_feature_views(self) -> List: - return self._on_demand_feature_views - - @property - def metadata(self) -> Optional[RetrievalMetadata]: - return self._metadata - - def persist( - self, - storage: SavedDatasetStorage, - allow_overwrite: bool = False, - timeout: Optional[int] = None, - ) -> None: - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - data_source = storage.to_data_source() - if not isinstance(data_source, MongoDBSource): - raise ValueError( - f"MongoDBNativeRetrievalJob.persist expected a MongoDBSource storage, " - f"got {type(data_source).__name__!r}." - ) - table = self._to_arrow_internal() - connection_string = self._config.offline_store.connection_string - db_name = data_source.database or self._config.offline_store.database - location = f"{db_name}.{data_source.collection}" - client: Any = MongoClient( - connection_string, driver=DRIVER_METADATA, tz_aware=True - ) - try: - coll = client[db_name][data_source.collection] - if not allow_overwrite and coll.estimated_document_count() > 0: - raise SavedDatasetLocationAlreadyExists(location=location) - coll.drop() - records = table.to_pylist() - if records: - coll.insert_many(records) - finally: - client.close() - - -class MongoDBOfflineStoreNative(OfflineStore): - """Offline store backed by MongoDB using native MQL aggregation pipelines. - - Compared with :class:`MongoDBOfflineStoreIbis`, this implementation avoids - the Ibis dependency entirely. The three main workflows map to: - - * ``offline_write_batch`` – Arrow → ``insert_many`` - * ``pull_latest_from_table_or_query`` – ``$match`` → ``$sort`` → ``$group`` - * ``pull_all_from_table_or_query`` – ``$match`` → ``$project`` - * ``get_historical_features`` – per-collection fetch + ``merge_asof`` - """ - - @staticmethod - def offline_write_batch( - config: RepoConfig, - feature_view: FeatureView, - table: pyarrow.Table, - progress: Optional[Callable[[int], Any]], - ) -> None: - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - data_source = feature_view.batch_source - if not isinstance(data_source, MongoDBSource): - raise ValueError( - f"MongoDBOfflineStoreNative.offline_write_batch expected a MongoDBSource, " - f"got {type(data_source).__name__!r}." - ) - connection_string = config.offline_store.connection_string - db_name = data_source.database or config.offline_store.database - records = table.to_pylist() - client: Any = MongoClient( - connection_string, driver=DRIVER_METADATA, tz_aware=True - ) - try: - coll = client[db_name][data_source.collection] - if records: - coll.insert_many(records) - if progress: - progress(len(records)) - finally: - client.close() - - @staticmethod - def pull_latest_from_table_or_query( - config: RepoConfig, - data_source: DataSource, - join_key_columns: List[str], - feature_name_columns: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str], - start_date: datetime, - end_date: datetime, - ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSource): - raise ValueError( - f"MongoDBOfflineStoreNative expected a MongoDBSource, " - f"got {type(data_source).__name__!r}." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - start_utc = start_date.astimezone(tz=timezone.utc) - end_utc = end_date.astimezone(tz=timezone.utc) - connection_string = config.offline_store.connection_string - db_name = data_source.database or config.offline_store.database - collection = data_source.collection - - # Sort by timestamp descending so $first in $group gets the latest document - sort_spec: Dict = {timestamp_field: -1} - if created_timestamp_column: - sort_spec[created_timestamp_column] = -1 - - # Group by entity/join keys. _id becomes a subdocument like {driver_id: 1}. - # $first grabs values from the first document in each group (the latest, - # due to prior $sort). - group_id = {k: f"${k}" for k in join_key_columns} - group_stage: Dict = { - "_id": group_id, - **{f: {"$first": f"${f}"} for f in feature_name_columns}, - timestamp_field: {"$first": f"${timestamp_field}"}, - } - if created_timestamp_column: - group_stage[created_timestamp_column] = { - "$first": f"${created_timestamp_column}" - } - - # Project to flatten the output: extract join keys from _id subdocument, - # include feature columns directly. Excludes the _id field from output. - project_stage: Dict = { - "_id": 0, - **{k: f"$_id.{k}" for k in join_key_columns}, - **{f: 1 for f in feature_name_columns}, - timestamp_field: 1, - } - if created_timestamp_column: - project_stage[created_timestamp_column] = 1 - - pipeline = [ - {"$match": {timestamp_field: {"$gte": start_utc, "$lte": end_utc}}}, - {"$sort": sort_spec}, - {"$group": group_stage}, - {"$project": project_stage}, - ] - - def _run() -> pyarrow.Table: - return _fetch_collection_as_arrow( - connection_string, db_name, collection, pipeline - ) - - return MongoDBNativeRetrievalJob( - query_fn=_run, - full_feature_names=False, - on_demand_feature_views=[], - metadata=None, - config=config, - ) - - @staticmethod - def pull_all_from_table_or_query( - config: RepoConfig, - data_source: DataSource, - join_key_columns: List[str], - feature_name_columns: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str] = None, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSource): - raise ValueError( - f"MongoDBOfflineStoreNative expected a MongoDBSource, " - f"got {type(data_source).__name__!r}." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - connection_string = config.offline_store.connection_string - db_name = data_source.database or config.offline_store.database - collection = data_source.collection - - fields = join_key_columns + feature_name_columns + [timestamp_field] - if created_timestamp_column: - fields.append(created_timestamp_column) - - match_filter: Dict = {} - if start_date or end_date: - ts_filter: Dict = {} - if start_date: - ts_filter["$gte"] = start_date.astimezone(tz=timezone.utc) - if end_date: - ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) - match_filter[timestamp_field] = ts_filter - - pipeline = [ - {"$match": match_filter}, - {"$project": {"_id": 0, **{f: 1 for f in fields}}}, - ] - - def _run() -> pyarrow.Table: - return _fetch_collection_as_arrow( - connection_string, db_name, collection, pipeline - ) - - return MongoDBNativeRetrievalJob( - query_fn=_run, - full_feature_names=False, - on_demand_feature_views=[], - metadata=None, - config=config, - ) - - @staticmethod - def get_historical_features( - config: RepoConfig, - feature_views: List[FeatureView], - feature_refs: List[str], - entity_df: Union[pd.DataFrame, str], - registry: BaseRegistry, - project: str, - full_feature_names: bool = False, - ) -> RetrievalJob: - if isinstance(entity_df, str): - raise ValueError( - "MongoDBOfflineStoreNative does not support SQL entity_df strings. " - "Pass a pandas DataFrame instead." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - connection_string = config.offline_store.connection_string - default_db = config.offline_store.database - - entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) - event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) - - # Map "feature_view:feature" refs → {fv_name: [feature, ...]} - fv_to_features: Dict[str, List[str]] = {} - for ref in feature_refs: - fv_name, feat_name = ref.split(":", 1) - fv_to_features.setdefault(fv_name, []).append(feat_name) - - fv_by_name = {fv.name: fv for fv in feature_views} - - def _run() -> pyarrow.Table: - result = entity_df.copy() - # Ensure the entity timestamp is tz-aware UTC for merge_asof - if result[event_timestamp_col].dt.tz is None: - result[event_timestamp_col] = pd.to_datetime( - result[event_timestamp_col], utc=True - ) - result = result.sort_values(event_timestamp_col) - - for fv_name, features in fv_to_features.items(): - fv = fv_by_name[fv_name] - source = fv.batch_source - if not isinstance(source, MongoDBSource): - raise ValueError( - f"MongoDBOfflineStoreNative: feature view {fv_name!r} has " - f"a non-MongoDBSource batch source ({type(source).__name__!r})." - ) - db_name = source.database or default_db - ts_field = source.timestamp_field - join_keys = [e.name for e in fv.entity_columns] - - arrow_table = _fetch_collection_as_arrow( - connection_string, db_name, source.collection - ) - if arrow_table.num_rows == 0: - for f in features: - col = f"{fv_name}__{f}" if full_feature_names else f - result[col] = None - continue - - feature_df = arrow_table.to_pandas() - # Ensure tz-aware UTC - if feature_df[ts_field].dt.tz is None: - feature_df[ts_field] = pd.to_datetime( - feature_df[ts_field], utc=True - ) - feature_df = feature_df.sort_values(ts_field) - - col_rename = { - f: (f"{fv_name}__{f}" if full_feature_names else f) - for f in features - } - cols_to_select = join_keys + features + [ts_field] - feature_df = feature_df[cols_to_select].rename(columns=col_rename) - out_features = list(col_rename.values()) - - merged = pd.merge_asof( - result, - feature_df, - left_on=event_timestamp_col, - right_on=ts_field, - by=join_keys, - direction="backward", - ) - # Apply TTL: null out features whose timestamp is too far in the past - if fv.ttl: - cutoff = merged[event_timestamp_col] - fv.ttl - too_old = merged[ts_field] < cutoff - for col in out_features: - merged.loc[too_old, col] = None - - result = merged.drop(columns=[ts_field], errors="ignore") - - return pyarrow.Table.from_pandas(result, preserve_index=False) - - return MongoDBNativeRetrievalJob( - query_fn=_run, - full_feature_names=full_feature_names, - on_demand_feature_views=[], - metadata=None, - config=config, - ) From afef4fdc561b9aadcb381e8d6c2911e5cf95f834 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 19 Mar 2026 14:12:59 -0400 Subject: [PATCH 18/76] Consolidate mongodb_source.py into mongodb.py - Move MongoDBSource, MongoDBOptions, SavedDatasetMongoDBStorage into mongodb.py - Move _infer_python_type_str helper into mongodb.py - Update imports in tests and benchmarks - Remove mongodb_source.py This consolidates the collection-per-FV implementation into a single file, making the codebase easier to navigate. Signed-off-by: Casey Clements --- design-notes/CASEY_SESSION_NOTES.md | 109 +++++++ design-notes/design-hybrid-with-batches.md | 239 +++++++++++++++ design-notes/native_implementation_notes.md | 191 ++++++++++++ design-notes/offline_store_design.md | 98 ++++++ ...ompt-mdb-fetch-pandas-join-with-batches.md | 108 +++++++ .../contrib/mongodb_offline_store/mongodb.py | 278 ++++++++++++++++- .../mongodb_offline_store/mongodb_source.py | 283 ------------------ .../benchmark_mongodb_offline_stores.py | 4 +- .../contrib/test_mongodb_offline_retrieval.py | 2 - 9 files changed, 1020 insertions(+), 292 deletions(-) create mode 100644 design-notes/CASEY_SESSION_NOTES.md create mode 100644 design-notes/design-hybrid-with-batches.md create mode 100644 design-notes/native_implementation_notes.md create mode 100644 design-notes/offline_store_design.md create mode 100644 design-notes/prompt-mdb-fetch-pandas-join-with-batches.md delete mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py diff --git a/design-notes/CASEY_SESSION_NOTES.md b/design-notes/CASEY_SESSION_NOTES.md new file mode 100644 index 00000000000..7a0b6f158f8 --- /dev/null +++ b/design-notes/CASEY_SESSION_NOTES.md @@ -0,0 +1,109 @@ +# MongoDB Feast Integration — Session Notes +_Last updated: 2026-03-16. Resume here after OS upgrade._ + +--- + +## Status at a Glance + +| Component | Branch | Status | +|---|---|---| +| **Online Store** | `INTPYTHON-297-MongoDB-Feast-Integration` | ✅ **Merged to upstream/master** | +| **Offline Store** | `FEAST-OfflineStore-INTPYTHON-297` | 🔧 In progress — next focus | + +--- + +## Online Store — COMPLETE ✅ + +### What was done +- Implemented `MongoDBOnlineStore` with full sync + async API +- Refactored write path: extracted `_build_write_ops` static method to eliminate code + duplication between `online_write_batch` and `online_write_batch_async` +- Added Feast driver metadata to MongoDB client instantiations +- Registered MongoDB in the feast-operator (kubebuilder enums, `ValidOnlineStoreDBStorePersistenceTypes`, operator YAMLs) +- Updated online store status from `alpha` → `preview` in docs +- All 5 unit tests pass (including Docker-based testcontainers integration test) + +### Key files +- `sdk/python/feast/infra/online_stores/mongodb_online_store/mongodb.py` — main implementation +- `sdk/python/tests/unit/online_store/test_mongodb_online_retrieval.py` — test suite +- `sdk/python/tests/universal/feature_repos/universal/online_store/mongodb.py` — universal test repo config + +### Git history cleanup (this session) +The PR had two merge commits (`632e103a6`, `26ce79b37`) that blocked squash-and-merge. +Resolution: +1. `git fetch --all` +2. Created clean branch `FEAST-OnlineStore-INTPYTHON-297` from `upstream/master` +3. Cherry-picked all 47 commits (oldest → newest), skipping the two merge commits +4. Resolved conflicts: directory rename (`tests/integration/` → `tests/universal/`), + `pixi.lock` auto-resolved, `detect-secrets` false positives got `# pragma: allowlist secret` +5. Force-pushed to `INTPYTHON-297-MongoDB-Feast-Integration` — maintainer squash-merged ✅ + +### Versioning +Version is derived dynamically via `setuptools_scm` from git tags (no hardcoded version). +Latest tag at time of merge: **`v0.60.0`**. Feature ships in the next release after that. +Update JIRA with the next release tag once the maintainers cut it. + +--- + +## Offline Store — IN PROGRESS 🔧 + +### Branch +``` +FEAST-OfflineStore-INTPYTHON-297 +``` + +### Commits on branch (not yet in upstream/master) +``` +cd3eef677 Started work on full Mongo/MQL implementation. Kept MongoDBOfflineStoreIbis and MongoDBOfflineStoreNative +71469f69a feat: restore test-python-universal-mongodb-online Makefile target +904505244 fix: pass onerror to pkgutil.walk_packages +946d84e4c fix: broaden import exception handling in doctest runner +55de0e9b5 fix: catch FeastExtrasDependencyImportError in doctest runner +157a71d77 refactor: improve MongoDB offline store code quality +67632af2f feat: Add MongoDB offline store (ibis-based PIT join, v1 alpha) +``` + +### Key files +- `sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py` + - Contains **two prototype implementations**: + - `MongoDBOfflineStoreIbis` — uses Ibis for point-in-time joins (delegates to `get_historical_features_ibis`) + - `MongoDBOfflineStoreNative` — native MQL implementation (started in `cd3eef677`, in progress) +- `sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py` — `MongoDBSource` data source + +### Architecture: Ibis vs Native +- **Ibis approach**: delegates PIT join to `feast.infra.offline_stores.ibis` helpers. + Pro: less code, consistency with other ibis-backed stores. + Con: requires ibis-mongodb connector; PIT correctness depends on ibis translation. +- **Native approach**: implements PIT join directly in MQL (MongoDB aggregation pipeline). + Pro: no extra dependency, full control. + Con: more complex; MQL aggregation pipelines can be verbose. +- Decision pending benchmarking / correctness validation between the two. + +### Next steps for offline store +1. Finish `MongoDBOfflineStoreNative` MQL implementation (started in latest commit) +2. Validate PIT correctness for both implementations against the Feast universal test suite +3. Run: `make test-python-universal-mongodb-offline` (target may need creating — see `71469f69a`) +4. Choose Ibis vs Native based on results; remove the other +5. Add to operator (same pattern as online store: kubebuilder enums, install.yaml) +6. Open PR — follow same DCO + linear history discipline as online store + +--- + +## Environment Notes + +- **Python env**: always use `uv run pytest ...` (uses `.venv` in repo root, Python 3.11) +- **Do NOT use**: system Python (`/Library/Frameworks/Python.framework/...`) or conda envs +- **Docker**: must be running for the testcontainers integration test +- **Stale container**: `72d14b345b6a` (mongo:latest, port 57120) — leftover from testing, safe to stop +- **DCO**: all commits must be signed: `git commit -s` +- **No push/merge without explicit user approval** + +--- + +## Git Workflow Reminder +To keep history clean (lesson from online store PR): +- Always branch from `upstream/master` (after `git fetch --all`) +- Never merge upstream into a feature branch — rebase or cherry-pick instead +- Before opening a PR, verify with: `git log --merges ^upstream/master --oneline` + (must return empty) + diff --git a/design-notes/design-hybrid-with-batches.md b/design-notes/design-hybrid-with-batches.md new file mode 100644 index 00000000000..080986579ef --- /dev/null +++ b/design-notes/design-hybrid-with-batches.md @@ -0,0 +1,239 @@ +Native MongoDB Offline Store (Hybrid Design) + +Design Document + +Overview + +This document describes the design of the Native MongoDB Offline Store for Feast using a hybrid execution model. The system combines MongoDB’s strengths in indexed data retrieval with Python’s strengths in relational and temporal joins. + +The implementation uses a single-collection schema in MongoDB to store feature data across all FeatureViews and performs point-in-time (PIT) joins using a “fetch + pandas join” strategy. This replaces an earlier fully in-database $lookup approach that proved unscalable for large workloads. + +The result is a design that is performant, scalable, and aligned with Feast’s semantics. + +⸻ + +Data Model + +All FeatureViews share a single MongoDB collection (feature_history). Each document represents an observation of a FeatureView for a given entity at a specific timestamp. + +Each document contains: + • A serialized entity identifier (entity_id) + • A FeatureView identifier (feature_view) + • A subdocument of feature values (features) + • An event timestamp (event_timestamp) + • An ingestion timestamp (created_at) + +This schema supports: + • Sparse feature storage (not all features present in every document) + • Flexible schema evolution over time + • Efficient indexing across FeatureViews + +A compound index is maintained on: + • (entity_id, feature_view, event_timestamp DESC) + +This index supports efficient filtering by entity, FeatureView, and time range. + +⸻ + +Execution Model + +High-Level Strategy + +The system implements historical feature retrieval in three stages: + 1. Preprocessing (Python) + • Normalize timestamps to UTC + • Serialize entity keys into entity_id + • Partition the input entity_df into manageable chunks + 2. Data Fetching (MongoDB) + • Query MongoDB using $in on entity IDs + • Filter by FeatureView and time bounds + • Retrieve matching feature documents in batches + 3. Point-in-Time Join (Python) + • Convert MongoDB results into pandas DataFrames + • Perform per-FeatureView joins using merge_asof + • Apply TTL constraints and feature selection + +This design avoids per-row database joins and instead performs a small number of efficient indexed scans. + +⸻ + +Chunking and Batching + +To ensure scalability, the system separates concerns between: + • Chunk size (entity_df) +Controls memory usage in Python +Default: ~5,000 rows + • Batch size (MongoDB queries) +Controls query size and index efficiency +Default: ~1,000 entity IDs per query + +Each chunk of entity_df is processed independently: + • Entity IDs are extracted and deduplicated + • Feature data is fetched in batches + • Results are joined and accumulated + +This ensures: + • Bounded memory usage + • Predictable query performance + • Compatibility with large workloads + +⸻ + +Point-in-Time Join Semantics + +For each FeatureView: + • Feature data is sorted by (entity_id, event_timestamp) + • The entity dataframe is similarly sorted + • A backward merge_asof is performed + +This ensures: + • Only feature values with timestamps ≤ entity timestamp are used + • The most recent valid feature value is selected + +TTL constraints are applied after the join: + • If the matched feature timestamp is older than the allowed TTL window, the value is set to NULL + +⸻ + +Key Improvements in Current Design + +1. Projection (Reduced Data Transfer) + +The system now explicitly limits fields retrieved from MongoDB to only those required: + • entity_id + • feature_view + • event_timestamp + • Requested feature fields within features + +This reduces: + • Network overhead + • BSON decoding cost + • Memory usage in pandas + +This is especially important for wide FeatureViews or large documents. + +⸻ + +2. Bounded Time Filtering + +Queries now include both: + • An upper bound (<= max_ts) + • A lower bound (>= min_ts) + +This significantly reduces the amount of historical data scanned when: + • The entity dataframe spans a narrow time window + • The feature store contains deep history + +This optimization improves: + • Query latency + • Index selectivity + • Memory footprint of retrieved data + +Future enhancements may incorporate TTL-aware lower bounds. + +⸻ + +3. Correct Sorting for Temporal Joins + +The system ensures proper sorting before merge_asof: + • Both dataframes are sorted by (entity_id, timestamp) + +This is critical for correctness when: + • Multiple entities are processed in a single batch + • Data is interleaved across entities + +Without this, joins may silently produce incorrect results. + +⸻ + +Tradeoffs + +Advantages + • Scalability: Avoids O(n × m) behavior of correlated joins + • Flexibility: Supports sparse and evolving schemas + • Performance: Leverages MongoDB indexes efficiently + • Simplicity: Uses well-understood pandas join semantics + +Limitations + • Memory-bound joins: Requires chunking for large workloads + • Multiple passes: Each FeatureView requires a separate join + • No server-side joins: MongoDB is used only for filtering, not relational logic + +⸻ + +Comparison to Alternative Designs + +Full MongoDB Join ($lookup) + +Rejected due to: + • Poor scaling with large entity sets + • Repeated execution of correlated subqueries + • High latency (orders of magnitude slower) + +⸻ + +Ibis-Based Design + • Uses one collection per FeatureView + • Loads data into memory and performs joins in Python + +Comparison: + • Similar performance after hybrid redesign + • Simpler query model + • Less flexible schema + +The Native design trades simplicity for: + • Unified storage + • Better alignment with document-based ingestion + • More flexible feature evolution + +⸻ + +Operational Considerations + +Index Management + +Indexes are created lazily at runtime: + • Ensures correctness without manual setup + • Avoids placing responsibility on users + +Future improvements may include: + • Optional strict index validation + • Configuration-driven index management + +⸻ + +MongoDB Client Usage + +Each chunk currently uses a separate MongoDB client instance. + +This is acceptable for moderate workloads but may be optimized in the future by: + • Reusing a shared client per retrieval job + • Leveraging connection pooling more explicitly + +⸻ + +Future Work + +Several enhancements are possible: + 1. Streaming Joins + • Avoid materializing all feature data in memory + • Process data incrementally + 2. Adaptive Chunking + • Dynamically adjust chunk size based on memory pressure + 3. TTL Pushdown + • Incorporate TTL constraints into MongoDB queries + 4. Parallel Execution + • Process chunks concurrently for large workloads + +⸻ + +Conclusion + +The hybrid MongoDB + pandas design represents a significant improvement over the initial fully in-database approach. It aligns system responsibilities with the strengths of each component: + • MongoDB handles indexed filtering and retrieval + • Python handles temporal join logic + +With the addition of projection, bounded time filtering, and correct sorting, the system is now both performant and correct for large-scale historical feature retrieval. + +This design provides a strong foundation for further optimization and production use. + diff --git a/design-notes/native_implementation_notes.md b/design-notes/native_implementation_notes.md new file mode 100644 index 00000000000..891751e56c2 --- /dev/null +++ b/design-notes/native_implementation_notes.md @@ -0,0 +1,191 @@ +# Native MongoDB Offline Store Implementation Review + +## Overview + +This document reviews the native MongoDB offline store implementation (`mongodb_native.py`) in the context of Feast idioms, the MongoDB online store implementation, and best practices. + +--- + +## Schema Alignment: Online ↔ Offline + +### Online Store Schema (mongodb_online_store/mongodb.py) +```javascript +{ + "_id": bytes, // serialized entity key + "features": { + "": { + "": value + } + }, + "event_timestamps": { "": datetime }, + "created_timestamp": datetime +} +``` + +### Offline Store Schema (Native) +```javascript +{ + "_id": ObjectId(), + "entity_id": bytes, // serialized entity key (same format as online _id) + "feature_view": "driver_stats", // discriminator + "features": { "": value }, + "event_timestamp": datetime, + "created_at": datetime +} +``` + +### ✅ Alignment Strengths +1. **Entity key serialization**: Both use `serialize_entity_key()` from `key_encoding_utils.py` +2. **Nested features**: Both use `features: { ... }` subdocument pattern +3. **Timestamps**: Both track event and created timestamps + +### ⚠️ Alignment Concerns +1. **`_id` usage**: Online uses `_id` = entity_id; Offline uses `_id` = ObjectId() with separate `entity_id` field + - **Recommendation**: Consider using `_id` = `{entity_id, feature_view, event_timestamp}` compound key for offline, eliminating ObjectId overhead + +2. **Feature nesting depth**: Online nests by feature_view then feature; Offline nests only by feature (feature_view is top-level) + - This is intentional (offline is one doc per event; online is one doc per entity with all FVs) + +--- + +## Feast Idioms Compliance + +### ✅ Correctly Followed +1. **RetrievalJob pattern**: Returns `MongoDBNativeRetrievalJob` wrapping a `query_fn` closure +2. **Arrow output**: `_to_arrow_internal()` returns `pyarrow.Table` (hard requirement) +3. **Warnings for preview**: Uses `warnings.warn()` with `RuntimeWarning` +4. **Config inheritance**: `MongoDBOfflineStoreNativeConfig` extends `FeastConfigBaseModel` +5. **DataSource pattern**: `MongoDBSourceNative` extends `DataSource` with `from_proto`/`_to_proto_impl` + +### ⚠️ Missing or Incomplete +1. **`offline_write_batch`**: Not implemented (raises `NotImplementedError` in persist) + - Required for push sources and `feast materialize` reverse path + - Should accept `pyarrow.Table` and insert into `feature_history` collection + +2. **`write_logged_features`**: Not implemented + - Lower priority but needed for feature logging + +3. **`persist()` on RetrievalJob**: Not implemented + - Should write results to a new collection for saved datasets + +--- + +## MQL Pipeline Quality + +### ✅ Well Implemented +1. **`pull_all_from_table_or_query`**: Clean range scan with `$project` flattening features server-side +2. **`pull_latest_from_table_or_query`**: Proper `$sort` → `$group` → `$project` pattern +3. **`get_historical_features`**: Uses `$lookup` with correlated subpipeline for server-side PIT join +4. **Per-FV TTL via `$switch`**: Elegant solution for different TTLs per feature view + +### ⚠️ Potential Improvements +1. **Index usage in `$lookup`**: The `$expr` in `$match` may not use indexes efficiently + - MongoDB 5.0+ has better support for `$expr` index usage + - Consider adding `hint` option if performance is critical + +2. **Temp collection cleanup**: Currently uses `try/finally` but could benefit from context manager pattern + +3. **Connection pooling**: Each method creates a new `MongoClient`. The online store caches `_client` and `_collection` + - **Recommendation**: Add `_client` caching to the offline store class or use connection pooling + +--- + +## Comparison with Online Store Patterns + +| Aspect | Online Store | Offline Store (Native) | +|--------|--------------|------------------------| +| Client caching | `_client`, `_collection` instance vars | New client per operation | +| Async support | Yes (`AsyncMongoClient`) | No | +| Batch operations | `bulk_write` with `UpdateOne` | `insert_many` | +| Error handling | Raises `RuntimeError` for config mismatch | Raises `ValueError` | +| DriverInfo | ✅ Yes | ✅ Yes | + +### Recommendations +1. **Add client caching** to avoid connection overhead per query +2. **Consider async support** for large entity_df scenarios +3. **Standardize error types** (use `RuntimeError` or `FeastError` subclasses) + +--- + +## Missing Features for Production Readiness + +### High Priority +1. **`offline_write_batch`**: Insert Arrow table into feature_history + ```python + @staticmethod + def offline_write_batch( + config: RepoConfig, + feature_view: FeatureView, + table: pyarrow.Table, + progress: Optional[Callable[[int], Any]], + ): + # Convert Arrow → docs with schema: + # { entity_id, feature_view, features: {...}, event_timestamp, created_at } + # Then insert_many() + ``` + +2. **Index creation helper**: Document or auto-create the compound index + ```javascript + db.feature_history.createIndex({ + entity_id: 1, + feature_view: 1, + event_timestamp: -1 + }) + ``` + +3. **Connection pooling / client reuse** + +### Medium Priority +4. **`persist()` for saved datasets**: Write retrieval results to a collection +5. **`write_logged_features`**: For feature logging support +6. **Async operations**: Mirror online store's async pattern + +### Lower Priority +7. **Streaming cursor support**: For very large result sets +8. **Explain plan logging**: Debug mode to show MQL execution plan + +--- + +## Code Quality Observations + +### ✅ Good +- Clear docstrings explaining schema and index requirements +- Type hints throughout +- Helper functions extracted (`_ttl_to_ms`, `_build_ttl_gte_expr`, `_serialize_entity_key_from_row`) +- Proper cleanup of temp collections in `finally` block + +### ⚠️ Could Improve +- Some duplication in timestamp timezone handling (could extract helper) +- Magic strings like `"event_timestamp"`, `"created_at"` could be constants +- The `_run()` closures are large — consider extracting to separate methods + +--- + +## Test Coverage Assessment + +Current tests cover: +- ✅ `pull_latest_from_table_or_query` +- ✅ `pull_all_from_table_or_query` +- ✅ `get_historical_features` (PIT join) +- ✅ TTL filtering +- ✅ Multiple feature views +- ✅ Compound join keys + +Missing tests: +- ❌ `offline_write_batch` (not implemented) +- ❌ Empty result handling edge cases +- ❌ Very large entity_df (performance/memory) +- ❌ Concurrent access to temp collections +- ❌ Index usage verification (explain plans) + +--- + +## Summary + +The native implementation is a solid foundation with proper use of MQL aggregation pipelines. Key next steps: + +1. **Implement `offline_write_batch`** — Required for push sources +2. **Add client caching** — Match online store pattern +3. **Document/automate index creation** — Critical for performance +4. **Consider `_id` schema optimization** — Use compound `_id` instead of ObjectId + entity_id + diff --git a/design-notes/offline_store_design.md b/design-notes/offline_store_design.md new file mode 100644 index 00000000000..fbe7120a3c1 --- /dev/null +++ b/design-notes/offline_store_design.md @@ -0,0 +1,98 @@ +# Corrected MongoDB OfflineStore Design + +## What the interface actually requires + +`RetrievalJob._to_arrow_internal` must return a `pyarrow.Table`. This is non-negotiable +because the compute engines call `retrieval_job.to_arrow()` directly: + +```python +# sdk/python/feast/infra/compute_engines/local/nodes.py +retrieval_job = create_offline_store_retrieval_job(...) +arrow_table = retrieval_job.to_arrow() # ← hard requirement +``` + +The compute engine then converts Arrow → proto tuples itself before calling +`OnlineStore.online_write_batch(data: List[Tuple[EntityKeyProto, ...]])`. +The offline store never sees the proto tuple format. + +`OfflineStore.offline_write_batch` (the push-source write path) takes a `pyarrow.Table` +— so Arrow is also the *input* format for writes. + +## The right approach — native aggregation, then Arrow + +The Couchbase offline store is the correct reference. It: +1. Expresses computation natively in the database (SQL++ window functions). +2. Iterates the cursor in Python. +3. Converts directly: `pa.Table.from_pylist(processed_rows)` — **no pandas intermediate**. + +MongoDB should follow the same pattern using its aggregation pipeline. + +## pull_latest_from_table_or_query + +The `$group` + `$sort` aggregation is the natural MongoDB equivalent of +`ROW_NUMBER() OVER(PARTITION BY entity ORDER BY timestamp DESC) = 1`: + +```python +pipeline = [ + {"$match": { + timestamp_field: {"$gte": start_date, "$lte": end_date} + }}, + {"$sort": {timestamp_field: -1, created_timestamp_column: -1}}, + {"$group": { + "_id": {k: f"${k}" for k in join_key_columns}, + **{f: {"$first": f"${f}"} for f in feature_name_columns}, + timestamp_field: {"$first": f"${timestamp_field}"}, + }}, +] +# cursor → pa.Table.from_pylist([doc for doc in collection.aggregate(pipeline)]) +``` + +No pandas. No Feast join utilities. The database does the work. + +## get_historical_features + +This is harder. The point-in-time join requires: for each (entity, entity_timestamp) row, +find the feature row with the latest `event_timestamp <= entity_timestamp`. + +MongoDB has no SQL window functions, but the aggregation pipeline can express this: + +``` +For each feature view: + $match: entity_ids in entity_df AND event_timestamp <= max(entity_timestamps) + $sort: entity_id, event_timestamp DESC + $lookup or unwind against entity_df rows + $match: event_timestamp <= entity_row.entity_timestamp (and TTL if set) + $group by (entity_id, entity_row_id): $first of features +``` + +This is complex but keeps computation in MongoDB and avoids loading the full history +into Python memory. The result cursor is then converted via `pa.Table.from_pylist()`. + +For an initial implementation it is acceptable to pull the filtered documents into +memory and do the join in Python (like the Dask store) — but this should be noted +as a known limitation, not the target design. + +## offline_write_batch + +Receives a `pyarrow.Table` from Feast (push-source path). Convert with +`table.to_pylist()` and `insert_many()` into the collection. + +## What changes from the previous design + +| Previous (incorrect) | Corrected | +|---------------------------------------------|---------------------------------------------| +| Pull docs into pandas, use offline_utils | Use MongoDB aggregation pipeline | +| pandas is the intermediate format | MongoDB cursor → `pa.Table.from_pylist()` | +| Arrow is an afterthought | Arrow is the required output of the job | +| Claimed online_write_batch takes Arrow | It takes proto tuples; compute engine converts | + +## Implementation order (unchanged) + +1. `MongoDBSource` — DataSource subclass (connection_string, database, collection, timestamp_field). +2. `MongoDBOfflineStoreConfig` — pydantic config. +3. `MongoDBRetrievalJob` — wraps aggregation pipeline, implements `_to_arrow_internal`. +4. `offline_write_batch` — `pyarrow.Table` → `insert_many`. +5. `pull_latest_from_table_or_query` — `$sort` + `$group` aggregation. +6. `pull_all_from_table_or_query` — `$match` time-range scan. +7. `get_historical_features` — aggregation pipeline PIT join (or in-memory fallback). + diff --git a/design-notes/prompt-mdb-fetch-pandas-join-with-batches.md b/design-notes/prompt-mdb-fetch-pandas-join-with-batches.md new file mode 100644 index 00000000000..9bd8fb437c3 --- /dev/null +++ b/design-notes/prompt-mdb-fetch-pandas-join-with-batches.md @@ -0,0 +1,108 @@ +Enhance MongoDBOfflineStoreNative.get_historical_features to support chunked execution for large entity_df, while preserving the existing fetch + pandas PIT join logic. + +Goals + • Prevent memory blowups for large entity_df + • Reuse the current implementation as much as possible + • Keep the code clean and idiomatic to Feast + +⸻ + +Requirements + +1. Add chunking based on entity_df size + • Introduce a constant: +``` python +CHUNK_SIZE = 5000 # make configurable configurable +``` + • If len(entity_df) <= CHUNK_SIZE: + • Run the existing _run() logic unchanged + • Else: + • Split entity_df into chunks of size CHUNK_SIZE + +⸻ + +2. Extract existing logic into reusable function +Refactor the current _run() implementation into a helper: +``` python +def _run_single(entity_subset_df: pd.DataFrame) -> pd.DataFrame: + ... +``` +This function should: + • Perform: + • entity_id serialization + • MongoDB fetch ($in query) + • pandas normalization + • per-feature-view merge_asof + • Return a pandas DataFrame (not Arrow) +3. Implement chunked execution +In _run(): +``` python +if len(entity_df) <= CHUNK_SIZE: + df = _run_single(entity_df) +else: + dfs = [] + for chunk in chunk_dataframe(entity_df, CHUNK_SIZE): + dfs.append(_run_single(chunk)) + df = pd.concat(dfs, ignore_index=True) +``` +4. Implement chunk helper +Add: +``` +def chunk_dataframe(df: pd.DataFrame, size: int): + for i in range(0, len(df), size): + yield df.iloc[i:i+size] +``` +5. Preserve ordering + • Ensure final DataFrame preserves original row order + • Use a _row_idx column if necessary +6. Handle edge cases +Ensure chunked version correctly handles: + • Empty MongoDB results + • Missing feature_views + • Missing features inside documents + • TTL filtering (already implemented in pandas) + +⸻ + +7. Return Arrow table +Final _run() must still return: +``` +pyarrow.Table.from_pandas(df, preserve_index=False) +``` +Constraints + • Do NOT reintroduce $lookup + • Do NOT use temp collections + • Do NOT duplicate large blocks of logic + • Keep code readable and maintainable + +⸻ + +Optional (nice-to-have) + • Add logging or debug print: + • number of chunks processed + • rows per chunk + +⸻ + +Outcome + • Small workloads behave exactly as before + • Large workloads are processed safely in chunks + • Performance remains close to Ibis for moderate sizes + • Memory usage is bounded + +⸻ + +🧠 Why this design is the right one + +This keeps your system: + +✅ Fast + • still uses vectorized joins + +✅ Scalable + • bounded memory + +✅ Clean + • no duplication + • no branching chaos + diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 10ffd6c5333..241e69cbb48 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -12,9 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json import warnings from datetime import datetime -from typing import Any, Callable, List, Optional, Union +from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union, cast import ibis import pandas as pd @@ -28,14 +29,12 @@ from feast.data_source import DataSource from feast.errors import ( + DataSourceNoNameException, FeastExtrasDependencyImportError, SavedDatasetLocationAlreadyExists, ) from feast.feature_view import FeatureView from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source import ( - MongoDBSource, -) from feast.infra.offline_stores.ibis import ( get_historical_features_ibis, pull_all_from_table_or_query_ibis, @@ -46,7 +45,278 @@ RetrievalJob, ) from feast.infra.registry.base_registry import BaseRegistry +from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto +from feast.protos.feast.core.SavedDataset_pb2 import ( + SavedDatasetStorage as SavedDatasetStorageProto, +) from feast.repo_config import FeastConfigBaseModel, RepoConfig +from feast.saved_dataset import SavedDatasetStorage +from feast.type_map import mongodb_to_feast_value_type +from feast.value_type import ValueType + +# --------------------------------------------------------------------------- +# Helper functions +# --------------------------------------------------------------------------- + + +def _infer_python_type_str(value: Any) -> Optional[str]: + """Infer a Feast-compatible type string from a Python value returned by pymongo.""" + if value is None: + return None + if isinstance(value, bool): + return "bool" + if isinstance(value, int): + return "int" + if isinstance(value, float): + return "float" + if isinstance(value, str): + return "str" + if isinstance(value, bytes): + return "bytes" + if isinstance(value, datetime): + return "datetime" + if isinstance(value, list): + if not value: + return "list[str]" + elem_type = _infer_python_type_str(value[0]) + if elem_type: + return f"list[{elem_type}]" + return "list[str]" + return None + + +# --------------------------------------------------------------------------- +# MongoDBSource and related classes (collection-per-FeatureView schema) +# --------------------------------------------------------------------------- + + +class MongoDBOptions: + """Options for a MongoDB data source (database + collection).""" + + def __init__(self, database: str, collection: str): + self._database = database + self._collection = collection + + def to_proto(self) -> DataSourceProto.CustomSourceOptions: + """Serialize database and collection names as JSON into a CustomSourceOptions proto.""" + return DataSourceProto.CustomSourceOptions( + configuration=json.dumps( + {"database": self._database, "collection": self._collection} + ).encode() + ) + + @classmethod + def from_proto( + cls, options_proto: DataSourceProto.CustomSourceOptions + ) -> "MongoDBOptions": + """Deserialize a CustomSourceOptions proto back into a MongoDBOptions instance.""" + config = json.loads(options_proto.configuration.decode("utf8")) + return cls(database=config["database"], collection=config["collection"]) + + +class MongoDBSource(DataSource): + """A MongoDB collection used as a Feast offline data source. + + ``name`` is the logical Feast name for this source. If omitted, it defaults + to the value of ``collection``. At least one of ``name`` or ``collection`` + must be supplied. + + ``database`` is the MongoDB database that contains the collection. When + omitted it falls back to ``MongoDBOfflineStoreConfig.database`` at query + time, so a single store-level default can be shared across many sources. + + ``schema_sample_size`` controls how many documents are randomly sampled + when Feast infers the collection schema (used by ``feast apply`` and + ``get_table_column_names_and_types``). Increase it for collections with + highly variable document shapes; decrease it to speed up ``feast apply`` + at the cost of schema coverage. + """ + + def source_type(self) -> DataSourceProto.SourceType.ValueType: + return DataSourceProto.CUSTOM_SOURCE + + def __init__( + self, + name: Optional[str] = None, + database: Optional[str] = None, + collection: Optional[str] = None, + timestamp_field: Optional[str] = "", + created_timestamp_column: Optional[str] = "", + field_mapping: Optional[Dict[str, str]] = None, + description: Optional[str] = "", + tags: Optional[Dict[str, str]] = None, + owner: Optional[str] = "", + schema_sample_size: int = 100, + ): + if name is None and collection is None: + raise DataSourceNoNameException() + # At least one of name / collection is non-None; cast to satisfy the type checker. + name = cast(str, name or collection) + + self._mongodb_options = MongoDBOptions( + database=database or "", + collection=collection or name, + ) + self._schema_sample_size = schema_sample_size + + super().__init__( + name=name, + timestamp_field=timestamp_field, + created_timestamp_column=created_timestamp_column, + field_mapping=field_mapping, + description=description, + tags=tags, + owner=owner, + ) + + def __hash__(self): + return super().__hash__() + + def __eq__(self, other): + if not isinstance(other, MongoDBSource): + raise TypeError( + "Comparisons should only involve MongoDBSource class objects." + ) + return ( + super().__eq__(other) + and self._mongodb_options._database == other._mongodb_options._database + and self._mongodb_options._collection == other._mongodb_options._collection + and self.timestamp_field == other.timestamp_field + and self.created_timestamp_column == other.created_timestamp_column + and self.field_mapping == other.field_mapping + ) + + @property + def database(self) -> str: + return self._mongodb_options._database + + @property + def collection(self) -> str: + return self._mongodb_options._collection + + @staticmethod + def from_proto(data_source: DataSourceProto) -> "MongoDBSource": + assert data_source.HasField("custom_options") + options = json.loads(data_source.custom_options.configuration) + return MongoDBSource( + name=data_source.name, + database=options["database"], + collection=options["collection"], + field_mapping=dict(data_source.field_mapping), + timestamp_field=data_source.timestamp_field, + created_timestamp_column=data_source.created_timestamp_column, + description=data_source.description, + tags=dict(data_source.tags), + owner=data_source.owner, + ) + + def _to_proto_impl(self) -> DataSourceProto: + data_source_proto = DataSourceProto( + name=self.name, + type=DataSourceProto.CUSTOM_SOURCE, + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBSource", + field_mapping=self.field_mapping, + custom_options=self._mongodb_options.to_proto(), + description=self.description, + tags=self.tags, + owner=self.owner, + ) + data_source_proto.timestamp_field = self.timestamp_field + data_source_proto.created_timestamp_column = self.created_timestamp_column + return data_source_proto + + def validate(self, config: RepoConfig): + # No upfront schema validation is required for MongoDB; the connection + # is exercised lazily when features are actually retrieved. + pass + + @staticmethod + def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: + return mongodb_to_feast_value_type + + def get_table_query_string(self) -> str: + return f"{self._mongodb_options._database}.{self._mongodb_options._collection}" + + def get_table_column_names_and_types( + self, config: RepoConfig + ) -> Iterable[Tuple[str, str]]: + """Sample documents from the collection to infer field names and their Feast type strings. + + Uses ``$sample`` to fetch up to ``schema_sample_size`` documents, then + picks the most-frequent Python type observed per field. The ``_id`` + field is always excluded. + """ + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + connection_string = config.offline_store.connection_string + db_name = self.database or config.offline_store.database + client: Any = MongoClient(connection_string, tz_aware=True) + try: + docs = list( + client[db_name][self.collection].aggregate( + [{"$sample": {"size": self._schema_sample_size}}] + ) + ) + finally: + client.close() + + field_type_counts: Dict[str, Dict[str, int]] = {} + for doc in docs: + for field, value in doc.items(): + if field == "_id": + continue + type_str = _infer_python_type_str(value) + if type_str is None: + continue + field_type_counts.setdefault(field, {}) + field_type_counts[field][type_str] = ( + field_type_counts[field].get(type_str, 0) + 1 + ) + + return [ + (field, max(counts, key=lambda t: counts[t])) + for field, counts in field_type_counts.items() + ] + + +class SavedDatasetMongoDBStorage(SavedDatasetStorage): + """Persists a Feast SavedDataset into a MongoDB collection.""" + + _proto_attr_name = "custom_storage" + + mongodb_options: MongoDBOptions + + def __init__(self, database: str, collection: str): + self.mongodb_options = MongoDBOptions( + database=database, + collection=collection, + ) + + @staticmethod + def from_proto( + storage_proto: SavedDatasetStorageProto, + ) -> "SavedDatasetMongoDBStorage": + options = json.loads(storage_proto.custom_storage.configuration) + return SavedDatasetMongoDBStorage( + database=options["database"], + collection=options["collection"], + ) + + def to_proto(self) -> SavedDatasetStorageProto: + return SavedDatasetStorageProto(custom_storage=self.mongodb_options.to_proto()) + + def to_data_source(self) -> DataSource: + return MongoDBSource( + database=self.mongodb_options._database, + collection=self.mongodb_options._collection, + ) + + +# --------------------------------------------------------------------------- +# Offline store configuration and implementation +# --------------------------------------------------------------------------- class MongoDBOfflineStoreIbisConfig(FeastConfigBaseModel): diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py deleted file mode 100644 index ee55fe24e6a..00000000000 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py +++ /dev/null @@ -1,283 +0,0 @@ -# Copyright 2026 The Feast Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -from datetime import datetime -from typing import Any, Callable, Dict, Iterable, Optional, Tuple, cast - -try: - from pymongo import MongoClient -except ImportError: - MongoClient = None # type: ignore[assignment,misc] - -from feast.data_source import DataSource -from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError -from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto -from feast.protos.feast.core.SavedDataset_pb2 import ( - SavedDatasetStorage as SavedDatasetStorageProto, -) -from feast.repo_config import RepoConfig -from feast.saved_dataset import SavedDatasetStorage -from feast.type_map import mongodb_to_feast_value_type -from feast.value_type import ValueType - - -def _infer_python_type_str(value: Any) -> Optional[str]: - """Infer a Feast-compatible type string from a Python value returned by pymongo.""" - if value is None: - return None - if isinstance(value, bool): - return "bool" - if isinstance(value, int): - return "int" - if isinstance(value, float): - return "float" - if isinstance(value, str): - return "str" - if isinstance(value, bytes): - return "bytes" - if isinstance(value, datetime): - return "datetime" - if isinstance(value, list): - if not value: - return "list[str]" - elem_type = _infer_python_type_str(value[0]) - if elem_type: - return f"list[{elem_type}]" - return "list[str]" - return None - - -class MongoDBOptions: - """Options for a MongoDB data source (database + collection).""" - - def __init__(self, database: str, collection: str): - self._database = database - self._collection = collection - - def to_proto(self) -> DataSourceProto.CustomSourceOptions: - """Serialize database and collection names as JSON into a CustomSourceOptions proto.""" - return DataSourceProto.CustomSourceOptions( - configuration=json.dumps( - {"database": self._database, "collection": self._collection} - ).encode() - ) - - @classmethod - def from_proto( - cls, options_proto: DataSourceProto.CustomSourceOptions - ) -> "MongoDBOptions": - """Deserialize a CustomSourceOptions proto back into a MongoDBOptions instance.""" - config = json.loads(options_proto.configuration.decode("utf8")) - return cls(database=config["database"], collection=config["collection"]) - - -class MongoDBSource(DataSource): - """A MongoDB collection used as a Feast offline data source. - - ``name`` is the logical Feast name for this source. If omitted, it defaults - to the value of ``collection``. At least one of ``name`` or ``collection`` - must be supplied. - - ``database`` is the MongoDB database that contains the collection. When - omitted it falls back to ``MongoDBOfflineStoreConfig.database`` at query - time, so a single store-level default can be shared across many sources. - - ``schema_sample_size`` controls how many documents are randomly sampled - when Feast infers the collection schema (used by ``feast apply`` and - ``get_table_column_names_and_types``). Increase it for collections with - highly variable document shapes; decrease it to speed up ``feast apply`` - at the cost of schema coverage. - """ - - def source_type(self) -> DataSourceProto.SourceType.ValueType: - return DataSourceProto.CUSTOM_SOURCE - - def __init__( - self, - name: Optional[str] = None, - database: Optional[str] = None, - collection: Optional[str] = None, - timestamp_field: Optional[str] = "", - created_timestamp_column: Optional[str] = "", - field_mapping: Optional[Dict[str, str]] = None, - description: Optional[str] = "", - tags: Optional[Dict[str, str]] = None, - owner: Optional[str] = "", - schema_sample_size: int = 100, - ): - if name is None and collection is None: - raise DataSourceNoNameException() - # At least one of name / collection is non-None; cast to satisfy the type checker. - name = cast(str, name or collection) - - self._mongodb_options = MongoDBOptions( - database=database or "", - collection=collection or name, - ) - self._schema_sample_size = schema_sample_size - - super().__init__( - name=name, - timestamp_field=timestamp_field, - created_timestamp_column=created_timestamp_column, - field_mapping=field_mapping, - description=description, - tags=tags, - owner=owner, - ) - - def __hash__(self): - return super().__hash__() - - def __eq__(self, other): - if not isinstance(other, MongoDBSource): - raise TypeError( - "Comparisons should only involve MongoDBSource class objects." - ) - return ( - super().__eq__(other) - and self._mongodb_options._database == other._mongodb_options._database - and self._mongodb_options._collection == other._mongodb_options._collection - and self.timestamp_field == other.timestamp_field - and self.created_timestamp_column == other.created_timestamp_column - and self.field_mapping == other.field_mapping - ) - - @property - def database(self) -> str: - return self._mongodb_options._database - - @property - def collection(self) -> str: - return self._mongodb_options._collection - - @staticmethod - def from_proto(data_source: DataSourceProto) -> "MongoDBSource": - assert data_source.HasField("custom_options") - options = json.loads(data_source.custom_options.configuration) - return MongoDBSource( - name=data_source.name, - database=options["database"], - collection=options["collection"], - field_mapping=dict(data_source.field_mapping), - timestamp_field=data_source.timestamp_field, - created_timestamp_column=data_source.created_timestamp_column, - description=data_source.description, - tags=dict(data_source.tags), - owner=data_source.owner, - ) - - def _to_proto_impl(self) -> DataSourceProto: - data_source_proto = DataSourceProto( - name=self.name, - type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source.MongoDBSource", - field_mapping=self.field_mapping, - custom_options=self._mongodb_options.to_proto(), - description=self.description, - tags=self.tags, - owner=self.owner, - ) - data_source_proto.timestamp_field = self.timestamp_field - data_source_proto.created_timestamp_column = self.created_timestamp_column - return data_source_proto - - def validate(self, config: RepoConfig): - # No upfront schema validation is required for MongoDB; the connection - # is exercised lazily when features are actually retrieved. - pass - - @staticmethod - def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: - return mongodb_to_feast_value_type - - def get_table_query_string(self) -> str: - return f"{self._mongodb_options._database}.{self._mongodb_options._collection}" - - def get_table_column_names_and_types( - self, config: RepoConfig - ) -> Iterable[Tuple[str, str]]: - """Sample documents from the collection to infer field names and their Feast type strings. - - Uses ``$sample`` to fetch up to ``schema_sample_size`` documents, then - picks the most-frequent Python type observed per field. The ``_id`` - field is always excluded. - """ - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - connection_string = config.offline_store.connection_string - db_name = self.database or config.offline_store.database - client: Any = MongoClient(connection_string, tz_aware=True) - try: - docs = list( - client[db_name][self.collection].aggregate( - [{"$sample": {"size": self._schema_sample_size}}] - ) - ) - finally: - client.close() - - field_type_counts: Dict[str, Dict[str, int]] = {} - for doc in docs: - for field, value in doc.items(): - if field == "_id": - continue - type_str = _infer_python_type_str(value) - if type_str is None: - continue - field_type_counts.setdefault(field, {}) - field_type_counts[field][type_str] = ( - field_type_counts[field].get(type_str, 0) + 1 - ) - - return [ - (field, max(counts, key=lambda t: counts[t])) - for field, counts in field_type_counts.items() - ] - - -class SavedDatasetMongoDBStorage(SavedDatasetStorage): - """Persists a Feast SavedDataset into a MongoDB collection.""" - - _proto_attr_name = "custom_storage" - - mongodb_options: MongoDBOptions - - def __init__(self, database: str, collection: str): - self.mongodb_options = MongoDBOptions( - database=database, - collection=collection, - ) - - @staticmethod - def from_proto( - storage_proto: SavedDatasetStorageProto, - ) -> "SavedDatasetMongoDBStorage": - options = json.loads(storage_proto.custom_storage.configuration) - return SavedDatasetMongoDBStorage( - database=options["database"], - collection=options["collection"], - ) - - def to_proto(self) -> SavedDatasetStorageProto: - return SavedDatasetStorageProto(custom_storage=self.mongodb_options.to_proto()) - - def to_data_source(self) -> DataSource: - return MongoDBSource( - database=self.mongodb_options._database, - collection=self.mongodb_options._collection, - ) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py b/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py index 177023dd6fb..27c6a6a35ae 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py @@ -37,15 +37,13 @@ from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb import ( MongoDBOfflineStoreIbis, MongoDBOfflineStoreIbisConfig, + MongoDBSource, ) from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( MongoDBOfflineStoreNative, MongoDBOfflineStoreNativeConfig, MongoDBSourceNative, ) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source import ( - MongoDBSource, -) from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto from feast.repo_config import RepoConfig diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py index 225d18d3e91..3acd93c2883 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py @@ -22,8 +22,6 @@ from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb import ( MongoDBOfflineStoreIbis, MongoDBOfflineStoreIbisConfig, -) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_source import ( MongoDBSource, ) from feast.repo_config import RepoConfig From f273744fad69ca74464110bea574f4e52ecd9f72 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 19 Mar 2026 14:27:34 -0400 Subject: [PATCH 19/76] Rename mongodb_offline_store to mongodb, use One/Many naming convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename module: mongodb_offline_store/ → mongodb/ - Rename files: mongodb.py → mongodb_many.py, mongodb_native.py → mongodb_one.py Class renames: - MongoDBSource → MongoDBSourceMany - MongoDBOptions → MongoDBOptionsMany - SavedDatasetMongoDBStorage → SavedDatasetMongoDBStorageMany - MongoDBOfflineStoreIbis → MongoDBOfflineStoreMany - MongoDBOfflineStoreIbisConfig → MongoDBOfflineStoreManyConfig - MongoDBSourceNative → MongoDBSourceOne - MongoDBOfflineStoreNative → MongoDBOfflineStoreOne - MongoDBOfflineStoreNativeConfig → MongoDBOfflineStoreOneConfig - MongoDBNativeRetrievalJob → MongoDBOneRetrievalJob The One/Many naming reflects the core architectural difference: - One: Single shared collection for all FeatureViews - Many: One collection per FeatureView Signed-off-by: Casey Clements --- .../__init__.py | 0 .../mongodb.py => mongodb/mongodb_many.py} | 72 ++++++++++--------- .../mongodb_one.py} | 56 ++++++++------- .../benchmark_mongodb_offline_stores.py | 40 +++++------ .../contrib/test_mongodb_offline_retrieval.py | 46 ++++++------ .../test_mongodb_offline_retrieval_native.py | 44 ++++++------ 6 files changed, 132 insertions(+), 126 deletions(-) rename sdk/python/feast/infra/offline_stores/contrib/{mongodb_offline_store => mongodb}/__init__.py (100%) rename sdk/python/feast/infra/offline_stores/contrib/{mongodb_offline_store/mongodb.py => mongodb/mongodb_many.py} (89%) rename sdk/python/feast/infra/offline_stores/contrib/{mongodb_offline_store/mongodb_native.py => mongodb/mongodb_one.py} (94%) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb/__init__.py similarity index 100% rename from sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py rename to sdk/python/feast/infra/offline_stores/contrib/mongodb/__init__.py diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_many.py similarity index 89% rename from sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py rename to sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_many.py index 241e69cbb48..7dac38af02b 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_many.py @@ -34,7 +34,7 @@ SavedDatasetLocationAlreadyExists, ) from feast.feature_view import FeatureView -from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA +from feast.infra.offline_stores.contrib.mongodb import DRIVER_METADATA from feast.infra.offline_stores.ibis import ( get_historical_features_ibis, pull_all_from_table_or_query_ibis, @@ -86,11 +86,11 @@ def _infer_python_type_str(value: Any) -> Optional[str]: # --------------------------------------------------------------------------- -# MongoDBSource and related classes (collection-per-FeatureView schema) +# MongoDBSourceMany and related classes (one collection per FeatureView) # --------------------------------------------------------------------------- -class MongoDBOptions: +class MongoDBOptionsMany: """Options for a MongoDB data source (database + collection).""" def __init__(self, database: str, collection: str): @@ -108,21 +108,21 @@ def to_proto(self) -> DataSourceProto.CustomSourceOptions: @classmethod def from_proto( cls, options_proto: DataSourceProto.CustomSourceOptions - ) -> "MongoDBOptions": - """Deserialize a CustomSourceOptions proto back into a MongoDBOptions instance.""" + ) -> "MongoDBOptionsMany": + """Deserialize a CustomSourceOptions proto back into a MongoDBOptionsMany instance.""" config = json.loads(options_proto.configuration.decode("utf8")) return cls(database=config["database"], collection=config["collection"]) -class MongoDBSource(DataSource): - """A MongoDB collection used as a Feast offline data source. +class MongoDBSourceMany(DataSource): + """A MongoDB collection used as a Feast offline data source (one collection per FeatureView). ``name`` is the logical Feast name for this source. If omitted, it defaults to the value of ``collection``. At least one of ``name`` or ``collection`` must be supplied. ``database`` is the MongoDB database that contains the collection. When - omitted it falls back to ``MongoDBOfflineStoreConfig.database`` at query + omitted it falls back to ``MongoDBOfflineStoreManyConfig.database`` at query time, so a single store-level default can be shared across many sources. ``schema_sample_size`` controls how many documents are randomly sampled @@ -153,7 +153,7 @@ def __init__( # At least one of name / collection is non-None; cast to satisfy the type checker. name = cast(str, name or collection) - self._mongodb_options = MongoDBOptions( + self._mongodb_options = MongoDBOptionsMany( database=database or "", collection=collection or name, ) @@ -173,9 +173,9 @@ def __hash__(self): return super().__hash__() def __eq__(self, other): - if not isinstance(other, MongoDBSource): + if not isinstance(other, MongoDBSourceMany): raise TypeError( - "Comparisons should only involve MongoDBSource class objects." + "Comparisons should only involve MongoDBSourceMany class objects." ) return ( super().__eq__(other) @@ -195,10 +195,10 @@ def collection(self) -> str: return self._mongodb_options._collection @staticmethod - def from_proto(data_source: DataSourceProto) -> "MongoDBSource": + def from_proto(data_source: DataSourceProto) -> "MongoDBSourceMany": assert data_source.HasField("custom_options") options = json.loads(data_source.custom_options.configuration) - return MongoDBSource( + return MongoDBSourceMany( name=data_source.name, database=options["database"], collection=options["collection"], @@ -214,7 +214,7 @@ def _to_proto_impl(self) -> DataSourceProto: data_source_proto = DataSourceProto( name=self.name, type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBSource", + data_source_class_type="feast.infra.offline_stores.contrib.mongodb.mongodb_many.MongoDBSourceMany", field_mapping=self.field_mapping, custom_options=self._mongodb_options.to_proto(), description=self.description, @@ -281,15 +281,15 @@ def get_table_column_names_and_types( ] -class SavedDatasetMongoDBStorage(SavedDatasetStorage): - """Persists a Feast SavedDataset into a MongoDB collection.""" +class SavedDatasetMongoDBStorageMany(SavedDatasetStorage): + """Persists a Feast SavedDataset into a MongoDB collection (many-collection schema).""" _proto_attr_name = "custom_storage" - mongodb_options: MongoDBOptions + mongodb_options: MongoDBOptionsMany def __init__(self, database: str, collection: str): - self.mongodb_options = MongoDBOptions( + self.mongodb_options = MongoDBOptionsMany( database=database, collection=collection, ) @@ -297,9 +297,9 @@ def __init__(self, database: str, collection: str): @staticmethod def from_proto( storage_proto: SavedDatasetStorageProto, - ) -> "SavedDatasetMongoDBStorage": + ) -> "SavedDatasetMongoDBStorageMany": options = json.loads(storage_proto.custom_storage.configuration) - return SavedDatasetMongoDBStorage( + return SavedDatasetMongoDBStorageMany( database=options["database"], collection=options["collection"], ) @@ -308,7 +308,7 @@ def to_proto(self) -> SavedDatasetStorageProto: return SavedDatasetStorageProto(custom_storage=self.mongodb_options.to_proto()) def to_data_source(self) -> DataSource: - return MongoDBSource( + return MongoDBSourceMany( database=self.mongodb_options._database, collection=self.mongodb_options._collection, ) @@ -319,10 +319,10 @@ def to_data_source(self) -> DataSource: # --------------------------------------------------------------------------- -class MongoDBOfflineStoreIbisConfig(FeastConfigBaseModel): - """Configuration for the MongoDB Ibis-backed offline store.""" +class MongoDBOfflineStoreManyConfig(FeastConfigBaseModel): + """Configuration for the MongoDB offline store (one collection per FeatureView).""" - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStoreIbis" + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb.mongodb_many.MongoDBOfflineStoreMany" """Offline store type selector""" connection_string: StrictStr = "mongodb://localhost:27017" @@ -332,8 +332,12 @@ class MongoDBOfflineStoreIbisConfig(FeastConfigBaseModel): """Default MongoDB database name""" -class MongoDBOfflineStoreIbis(OfflineStore): - """Offline store backed by MongoDB, using Ibis for point-in-time joins.""" +class MongoDBOfflineStoreMany(OfflineStore): + """Offline store backed by MongoDB (one collection per FeatureView). + + Uses Ibis memtables for point-in-time joins. Each FeatureView's data is stored + in a separate MongoDB collection, with the collection name matching the source name. + """ @staticmethod def pull_latest_from_table_or_query( @@ -346,9 +350,9 @@ def pull_latest_from_table_or_query( start_date: datetime, end_date: datetime, ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSource): + if not isinstance(data_source, MongoDBSourceMany): raise ValueError( - f"MongoDBOfflineStore expected a MongoDBSource, " + f"MongoDBOfflineStoreMany expected a MongoDBSourceMany, " f"got {type(data_source).__name__!r}." ) warnings.warn( @@ -405,9 +409,9 @@ def pull_all_from_table_or_query( start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSource): + if not isinstance(data_source, MongoDBSourceMany): raise ValueError( - f"MongoDBOfflineStore expected a MongoDBSource, " + f"MongoDBOfflineStoreMany expected a MongoDBSourceMany, " f"got {type(data_source).__name__!r}." ) warnings.warn( @@ -436,9 +440,9 @@ def reader(data_source: DataSource, repo_path: str) -> Table: raise FeastExtrasDependencyImportError( "mongodb", "pymongo is not installed." ) - if not isinstance(data_source, MongoDBSource): + if not isinstance(data_source, MongoDBSourceMany): raise ValueError( - f"MongoDBOfflineStore reader expected a MongoDBSource, " + f"MongoDBOfflineStoreMany reader expected a MongoDBSourceMany, " f"got {type(data_source).__name__!r}." ) connection_string = config.offline_store.connection_string @@ -487,9 +491,9 @@ def writer( raise FeastExtrasDependencyImportError( "mongodb", "pymongo is not installed." ) - if not isinstance(data_source, MongoDBSource): + if not isinstance(data_source, MongoDBSourceMany): raise ValueError( - f"MongoDBOfflineStore writer expected a MongoDBSource, " + f"MongoDBOfflineStoreMany writer expected a MongoDBSourceMany, " f"got {type(data_source).__name__!r}." ) connection_string = config.offline_store.connection_string diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_one.py similarity index 94% rename from sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py rename to sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_one.py index aa0c88f0331..293b785c868 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_one.py @@ -109,7 +109,7 @@ from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError from feast.feature_view import FeatureView from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA +from feast.infra.offline_stores.contrib.mongodb import DRIVER_METADATA from feast.infra.offline_stores.offline_store import ( OfflineStore, RetrievalJob, @@ -127,10 +127,12 @@ from feast.value_type import ValueType -class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): - """Configuration for the Native MongoDB offline store.""" +class MongoDBOfflineStoreOneConfig(FeastConfigBaseModel): + """Configuration for the MongoDB offline store (single shared collection).""" - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBOfflineStoreNative" + type: StrictStr = ( + "feast.infra.offline_stores.contrib.mongodb.mongodb_one.MongoDBOfflineStoreOne" + ) """Offline store type selector""" connection_string: StrictStr = "mongodb://localhost:27017" @@ -143,12 +145,12 @@ class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): """Single collection name for all feature views""" -class MongoDBSourceNative(DataSource): - """A MongoDB data source for the native offline store. +class MongoDBSourceOne(DataSource): + """A MongoDB data source for the single-collection offline store. - Unlike many data source implementations, this source does not map each - FeatureView to its own table or collection. Instead, all FeatureViews - share a single MongoDB collection (configured at the store level). + Unlike MongoDBSourceMany, this source does not map each FeatureView to + its own collection. Instead, all FeatureViews share a single MongoDB + collection (configured at the store level). Each document in that collection includes a ``feature_view`` field that identifies which FeatureView it belongs to. The ``name`` of this data @@ -183,9 +185,9 @@ def __hash__(self): return super().__hash__() def __eq__(self, other): - if not isinstance(other, MongoDBSourceNative): + if not isinstance(other, MongoDBSourceOne): raise TypeError( - "Comparisons should only involve MongoDBSourceNative class objects." + "Comparisons should only involve MongoDBSourceOne class objects." ) return ( super().__eq__(other) @@ -203,9 +205,9 @@ def source_type(self) -> DataSourceProto.SourceType.ValueType: return DataSourceProto.CUSTOM_SOURCE @staticmethod - def from_proto(data_source: DataSourceProto) -> "MongoDBSourceNative": + def from_proto(data_source: DataSourceProto) -> "MongoDBSourceOne": assert data_source.HasField("custom_options") - return MongoDBSourceNative( + return MongoDBSourceOne( name=data_source.name, timestamp_field=data_source.timestamp_field, created_timestamp_column=data_source.created_timestamp_column, @@ -219,7 +221,7 @@ def _to_proto_impl(self) -> DataSourceProto: return DataSourceProto( name=self.name, type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBSourceNative", + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBSourceOne", field_mapping=self.field_mapping, custom_options=DataSourceProto.CustomSourceOptions( configuration=json.dumps({"feature_view": self.name}).encode() @@ -320,7 +322,7 @@ def _fetch_documents( return list(client[database][collection].aggregate(pipeline)) -class MongoDBNativeRetrievalJob(RetrievalJob): +class MongoDBOneRetrievalJob(RetrievalJob): """Retrieval job for native MongoDB offline store queries.""" def __init__( @@ -384,7 +386,7 @@ def _serialize_entity_key_from_row( return serialize_entity_key(entity_key, entity_key_serialization_version) -class MongoDBOfflineStoreNative(OfflineStore): +class MongoDBOfflineStoreOne(OfflineStore): """Native MongoDB offline store using single-collection schema. All feature views share one collection (``feature_history``), with documents @@ -452,9 +454,9 @@ def pull_latest_from_table_or_query( start_date: datetime, end_date: datetime, ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSourceNative): + if not isinstance(data_source, MongoDBSourceOne): raise ValueError( - f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " + f"MongoDBOfflineStoreOne expected MongoDBSourceOne, " f"got {type(data_source).__name__!r}." ) warnings.warn( @@ -499,7 +501,7 @@ def pull_latest_from_table_or_query( ] def _run() -> pyarrow.Table: - client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + client = MongoDBOfflineStoreOne._get_client_and_ensure_indexes(config) try: docs = _fetch_documents(client, db_name, collection, pipeline) if not docs: @@ -515,7 +517,7 @@ def _run() -> pyarrow.Table: finally: client.close() - return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) + return MongoDBOneRetrievalJob(query_fn=_run, full_feature_names=False) @staticmethod def pull_all_from_table_or_query( @@ -528,9 +530,9 @@ def pull_all_from_table_or_query( start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSourceNative): + if not isinstance(data_source, MongoDBSourceOne): raise ValueError( - f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " + f"MongoDBOfflineStoreOne expected MongoDBSourceOne, " f"got {type(data_source).__name__!r}." ) warnings.warn( @@ -571,7 +573,7 @@ def pull_all_from_table_or_query( ] def _run() -> pyarrow.Table: - client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + client = MongoDBOfflineStoreOne._get_client_and_ensure_indexes(config) try: docs = _fetch_documents(client, db_name, collection, pipeline) if not docs: @@ -587,7 +589,7 @@ def _run() -> pyarrow.Table: finally: client.close() - return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) + return MongoDBOneRetrievalJob(query_fn=_run, full_feature_names=False) @staticmethod def get_historical_features( @@ -610,7 +612,7 @@ def get_historical_features( """ if isinstance(entity_df, str): raise ValueError( - "MongoDBOfflineStoreNative does not support SQL entity_df strings. " + "MongoDBOfflineStoreOne does not support SQL entity_df strings. " "Pass a pandas DataFrame instead." ) warnings.warn( @@ -777,7 +779,7 @@ def _run() -> pyarrow.Table: working_df["_row_idx"] = range(len(working_df)) # Create client once for all chunks - client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + client = MongoDBOfflineStoreOne._get_client_and_ensure_indexes(config) try: coll = client[db_name][feature_collection] @@ -807,7 +809,7 @@ def _run() -> pyarrow.Table: return pyarrow.Table.from_pandas(result_df, preserve_index=False) - return MongoDBNativeRetrievalJob( + return MongoDBOneRetrievalJob( query_fn=_run, full_feature_names=full_feature_names, ) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py b/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py index 27c6a6a35ae..3b663b150c7 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py @@ -34,15 +34,15 @@ from feast import Entity, FeatureView, Field from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb import ( - MongoDBOfflineStoreIbis, - MongoDBOfflineStoreIbisConfig, - MongoDBSource, +from feast.infra.offline_stores.contrib.mongodb.mongodb_many import ( + MongoDBOfflineStoreMany, + MongoDBOfflineStoreManyConfig, + MongoDBSourceMany, ) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( - MongoDBOfflineStoreNative, - MongoDBOfflineStoreNativeConfig, - MongoDBSourceNative, +from feast.infra.offline_stores.contrib.mongodb.mongodb_one import ( + MongoDBOfflineStoreOne, + MongoDBOfflineStoreOneConfig, + MongoDBSourceOne, ) from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto @@ -149,7 +149,7 @@ def ibis_config(mongodb_connection_string: str) -> RepoConfig: project="benchmark", registry="memory://", provider="local", - offline_store=MongoDBOfflineStoreIbisConfig( + offline_store=MongoDBOfflineStoreManyConfig( connection_string=mongodb_connection_string, database="benchmark_db", ), @@ -165,7 +165,7 @@ def native_config(mongodb_connection_string: str) -> RepoConfig: project="benchmark", registry="memory://", provider="local", - offline_store=MongoDBOfflineStoreNativeConfig( + offline_store=MongoDBOfflineStoreOneConfig( connection_string=mongodb_connection_string, database="benchmark_db", collection="feature_history", @@ -241,7 +241,7 @@ def _generate_native_data( def _create_ibis_fv(num_features: int) -> tuple: """Create Ibis source and FeatureView.""" - source = MongoDBSource( + source = MongoDBSourceMany( name="driver_benchmark", database="benchmark_db", collection="driver_benchmark", @@ -267,7 +267,7 @@ def _create_ibis_fv(num_features: int) -> tuple: def _create_native_fv(num_features: int) -> tuple: """Create Native source and FeatureView.""" - source = MongoDBSourceNative( + source = MongoDBSourceOne( name="driver_benchmark", timestamp_field="event_timestamp", ) @@ -406,7 +406,7 @@ def test_scale_rows_ibis( feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] def run_query(): - job = MongoDBOfflineStoreIbis.get_historical_features( + job = MongoDBOfflineStoreMany.get_historical_features( config=ibis_config, feature_views=[fv], feature_refs=feature_refs, @@ -461,7 +461,7 @@ def test_scale_rows_native( feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] def run_query(): - job = MongoDBOfflineStoreNative.get_historical_features( + job = MongoDBOfflineStoreOne.get_historical_features( config=native_config, feature_views=[fv], feature_refs=feature_refs, @@ -517,7 +517,7 @@ def test_wide_features_ibis( feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] def run_query(): - job = MongoDBOfflineStoreIbis.get_historical_features( + job = MongoDBOfflineStoreMany.get_historical_features( config=ibis_config, feature_views=[fv], feature_refs=feature_refs, @@ -570,7 +570,7 @@ def test_wide_features_native( feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] def run_query(): - job = MongoDBOfflineStoreNative.get_historical_features( + job = MongoDBOfflineStoreOne.get_historical_features( config=native_config, feature_views=[fv], feature_refs=feature_refs, @@ -637,7 +637,7 @@ def test_entity_skew_ibis( feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] def run_query(): - job = MongoDBOfflineStoreIbis.get_historical_features( + job = MongoDBOfflineStoreMany.get_historical_features( config=ibis_config, feature_views=[fv], feature_refs=feature_refs, @@ -703,7 +703,7 @@ def test_entity_skew_native( feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] def run_query(): - job = MongoDBOfflineStoreNative.get_historical_features( + job = MongoDBOfflineStoreOne.get_historical_features( config=native_config, feature_views=[fv], feature_refs=feature_refs, @@ -776,7 +776,7 @@ def test_summary_comparison( _, ibis_fv = _create_ibis_fv(num_features) def run_ibis(): - job = MongoDBOfflineStoreIbis.get_historical_features( + job = MongoDBOfflineStoreMany.get_historical_features( config=ibis_config, feature_views=[ibis_fv], feature_refs=feature_refs, @@ -793,7 +793,7 @@ def run_ibis(): _, native_fv = _create_native_fv(num_features) def run_native(): - job = MongoDBOfflineStoreNative.get_historical_features( + job = MongoDBOfflineStoreOne.get_historical_features( config=native_config, feature_views=[native_fv], feature_refs=feature_refs, diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py index 3acd93c2883..1c9882900d5 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py @@ -19,10 +19,10 @@ from testcontainers.mongodb import MongoDbContainer from feast import Entity, FeatureView, Field -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb import ( - MongoDBOfflineStoreIbis, - MongoDBOfflineStoreIbisConfig, - MongoDBSource, +from feast.infra.offline_stores.contrib.mongodb.mongodb_many import ( + MongoDBOfflineStoreMany, + MongoDBOfflineStoreManyConfig, + MongoDBSourceMany, ) from feast.repo_config import RepoConfig from feast.types import Float64, Int64, String @@ -75,7 +75,7 @@ def repo_config(mongodb_connection_string: str) -> RepoConfig: project="test_project", registry="memory://", provider="local", - offline_store=MongoDBOfflineStoreIbisConfig( + offline_store=MongoDBOfflineStoreManyConfig( connection_string=mongodb_connection_string, database="feast_test", ), @@ -129,9 +129,9 @@ def sample_data(mongodb_connection_string: str) -> datetime: @pytest.fixture -def driver_source() -> MongoDBSource: - """Create a MongoDBSource for driver stats.""" - return MongoDBSource( +def driver_source() -> MongoDBSourceMany: + """Create a MongoDBSourceMany for driver stats.""" + return MongoDBSourceMany( name="driver_stats", database="feast_test", collection="driver_stats", @@ -140,7 +140,7 @@ def driver_source() -> MongoDBSource: @pytest.fixture -def driver_fv(driver_source: MongoDBSource) -> FeatureView: +def driver_fv(driver_source: MongoDBSourceMany) -> FeatureView: """Create a FeatureView for driver stats. The ttl (time-to-live) parameter defines how far back in time Feast will look @@ -170,7 +170,7 @@ def driver_fv(driver_source: MongoDBSource) -> FeatureView: @_requires_docker def test_pull_latest_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSource + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceMany ) -> None: """Test pulling latest features per entity from MongoDB. @@ -180,7 +180,7 @@ def test_pull_latest_from_table_or_query( is from 2 hours ago. """ now = sample_data - job = MongoDBOfflineStoreIbis.pull_latest_from_table_or_query( + job = MongoDBOfflineStoreMany.pull_latest_from_table_or_query( config=repo_config, data_source=driver_source, join_key_columns=["driver_id"], @@ -256,7 +256,7 @@ def test_get_historical_features_pit_join( } ) - job = MongoDBOfflineStoreIbis.get_historical_features( + job = MongoDBOfflineStoreMany.get_historical_features( config=repo_config, feature_views=[driver_fv], feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], @@ -287,11 +287,11 @@ def test_get_historical_features_pit_join( @_requires_docker def test_pull_all_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSource + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceMany ) -> None: """Test pulling all features within a time range (no deduplication).""" now = sample_data - job = MongoDBOfflineStoreIbis.pull_all_from_table_or_query( + job = MongoDBOfflineStoreMany.pull_all_from_table_or_query( config=repo_config, data_source=driver_source, join_key_columns=["driver_id"], @@ -314,7 +314,7 @@ def test_pull_all_from_table_or_query( def test_ttl_excludes_stale_features( repo_config: RepoConfig, mongodb_connection_string: str, - driver_source: MongoDBSource, + driver_source: MongoDBSourceMany, ) -> None: """Test that TTL causes stale feature values to be returned as NULL. @@ -339,7 +339,7 @@ def test_ttl_excludes_stale_features( client.close() # Create source and feature view with 1-day TTL - ttl_source = MongoDBSource( + ttl_source = MongoDBSourceMany( name="driver_stats_ttl_test", database="feast_test", collection="driver_stats_ttl_test", @@ -367,7 +367,7 @@ def test_ttl_excludes_stale_features( } ) - job = MongoDBOfflineStoreIbis.get_historical_features( + job = MongoDBOfflineStoreMany.get_historical_features( config=repo_config, feature_views=[ttl_fv], feature_refs=["driver_stats_ttl_test:conv_rate"], @@ -429,13 +429,13 @@ def test_multiple_feature_views( client.close() # Create sources for each collection - driver_source = MongoDBSource( + driver_source = MongoDBSourceMany( name="driver_stats_multi", database="feast_test", collection="driver_stats_multi", timestamp_field="event_timestamp", ) - vehicle_source = MongoDBSource( + vehicle_source = MongoDBSourceMany( name="vehicle_stats_multi", database="feast_test", collection="vehicle_stats_multi", @@ -481,7 +481,7 @@ def test_multiple_feature_views( ) # Request features from BOTH feature views - job = MongoDBOfflineStoreIbis.get_historical_features( + job = MongoDBOfflineStoreMany.get_historical_features( config=repo_config, feature_views=[driver_fv, vehicle_fv], feature_refs=[ @@ -566,7 +566,7 @@ def test_compound_join_keys( client.close() # Create source - source = MongoDBSource( + source = MongoDBSourceMany( name="user_device_features", database="feast_test", collection="user_device_features", @@ -594,7 +594,7 @@ def test_compound_join_keys( ) # Test pull_latest: should get one row per unique (user_id, device_id) combination - job = MongoDBOfflineStoreIbis.pull_latest_from_table_or_query( + job = MongoDBOfflineStoreMany.pull_latest_from_table_or_query( config=repo_config, data_source=source, join_key_columns=["user_id", "device_id"], @@ -622,7 +622,7 @@ def test_compound_join_keys( } ) - job = MongoDBOfflineStoreIbis.get_historical_features( + job = MongoDBOfflineStoreMany.get_historical_features( config=repo_config, feature_views=[fv], feature_refs=["user_device_features:app_opens"], diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py index 5c022992548..f18d2d15af9 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py @@ -32,10 +32,10 @@ from feast import Entity, FeatureView, Field from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( - MongoDBOfflineStoreNative, - MongoDBOfflineStoreNativeConfig, - MongoDBSourceNative, +from feast.infra.offline_stores.contrib.mongodb.mongodb_one import ( + MongoDBOfflineStoreOne, + MongoDBOfflineStoreOneConfig, + MongoDBSourceOne, ) from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto @@ -109,7 +109,7 @@ def repo_config(mongodb_connection_string: str) -> RepoConfig: project="test_project", registry="memory://", provider="local", - offline_store=MongoDBOfflineStoreNativeConfig( + offline_store=MongoDBOfflineStoreOneConfig( connection_string=mongodb_connection_string, database="feast_test", collection="feature_history", @@ -170,9 +170,9 @@ def sample_data(mongodb_connection_string: str) -> datetime: @pytest.fixture -def driver_source() -> MongoDBSourceNative: - """Create a MongoDBSourceNative for driver stats.""" - return MongoDBSourceNative( +def driver_source() -> MongoDBSourceOne: + """Create a MongoDBSourceOne for driver stats.""" + return MongoDBSourceOne( name="driver_stats", timestamp_field="event_timestamp", created_timestamp_column="created_at", @@ -180,7 +180,7 @@ def driver_source() -> MongoDBSourceNative: @pytest.fixture -def driver_fv(driver_source: MongoDBSourceNative) -> FeatureView: +def driver_fv(driver_source: MongoDBSourceOne) -> FeatureView: """Create a FeatureView for driver stats.""" driver_entity = Entity( name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 @@ -200,11 +200,11 @@ def driver_fv(driver_source: MongoDBSourceNative) -> FeatureView: @_requires_docker def test_pull_latest_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceNative + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceOne ) -> None: """Test pulling latest features per entity from the single collection.""" now = sample_data - job = MongoDBOfflineStoreNative.pull_latest_from_table_or_query( + job = MongoDBOfflineStoreOne.pull_latest_from_table_or_query( config=repo_config, data_source=driver_source, join_key_columns=["driver_id"], @@ -246,7 +246,7 @@ def test_get_historical_features_pit_join( } ) - job = MongoDBOfflineStoreNative.get_historical_features( + job = MongoDBOfflineStoreOne.get_historical_features( config=repo_config, feature_views=[driver_fv], feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], @@ -277,11 +277,11 @@ def test_get_historical_features_pit_join( @_requires_docker def test_pull_all_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceNative + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceOne ) -> None: """Test pulling all features within a time range (no deduplication).""" now = sample_data - job = MongoDBOfflineStoreNative.pull_all_from_table_or_query( + job = MongoDBOfflineStoreOne.pull_all_from_table_or_query( config=repo_config, data_source=driver_source, join_key_columns=["driver_id"], @@ -330,7 +330,7 @@ def test_ttl_excludes_stale_features( collection.insert_many(ttl_docs) client.close() - ttl_source = MongoDBSourceNative( + ttl_source = MongoDBSourceOne( name="driver_stats_ttl", timestamp_field="event_timestamp", ) @@ -355,7 +355,7 @@ def test_ttl_excludes_stale_features( } ) - job = MongoDBOfflineStoreNative.get_historical_features( + job = MongoDBOfflineStoreOne.get_historical_features( config=repo_config, feature_views=[ttl_fv], feature_refs=["driver_stats_ttl:conv_rate"], @@ -422,8 +422,8 @@ def test_multiple_feature_views( client.close() # Create sources and feature views - driver_source = MongoDBSourceNative(name="driver_stats_multi") - vehicle_source = MongoDBSourceNative(name="vehicle_stats_multi") + driver_source = MongoDBSourceOne(name="driver_stats_multi") + vehicle_source = MongoDBSourceOne(name="vehicle_stats_multi") driver_entity = Entity( name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 @@ -459,7 +459,7 @@ def test_multiple_feature_views( } ) - job = MongoDBOfflineStoreNative.get_historical_features( + job = MongoDBOfflineStoreOne.get_historical_features( config=repo_config, feature_views=[driver_fv, vehicle_fv], feature_refs=[ @@ -534,7 +534,7 @@ def test_compound_join_keys( collection.insert_many(compound_docs) client.close() - source = MongoDBSourceNative(name="user_device_features") + source = MongoDBSourceOne(name="user_device_features") user_entity = Entity( name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 @@ -556,7 +556,7 @@ def test_compound_join_keys( ) # Test pull_latest: should get one row per unique (user_id, device_id) - job = MongoDBOfflineStoreNative.pull_latest_from_table_or_query( + job = MongoDBOfflineStoreOne.pull_latest_from_table_or_query( config=repo_config, data_source=source, join_key_columns=["user_id", "device_id"], @@ -585,7 +585,7 @@ def test_compound_join_keys( } ) - job = MongoDBOfflineStoreNative.get_historical_features( + job = MongoDBOfflineStoreOne.get_historical_features( config=repo_config, feature_views=[fv], feature_refs=["user_device_features:app_opens"], From df00e475a067f45adcb50417941dc7c1d90b43fc Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 14:14:49 -0400 Subject: [PATCH 20/76] Add README.md documenting MongoDB offline store implementations Signed-off-by: Casey Clements --- .../offline_stores/contrib/mongodb/README.md | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb/README.md diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb/README.md b/sdk/python/feast/infra/offline_stores/contrib/mongodb/README.md new file mode 100644 index 00000000000..44983940ff2 --- /dev/null +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb/README.md @@ -0,0 +1,143 @@ +# MongoDB Offline Store + +Two MongoDB offline store implementations optimized for different use cases. + +## Overview + +| Aspect | `MongoDBOfflineStoreMany` | `MongoDBOfflineStoreOne` | +|--------|---------------------------|--------------------------| +| Collections | One per FeatureView | Single shared collection | +| Schema | Flat documents | Nested `features` subdoc | +| Entity ID | Separate columns | Serialized bytes | +| Best for | Small-medium feature stores | Large feature stores | + +## MongoDBOfflineStoreMany (mongodb_many.py) + +**One collection per FeatureView** — each FeatureView maps to its own MongoDB collection. + +### Schema + +```javascript +// Collection: driver_stats +{ + "driver_id": 1001, + "event_timestamp": ISODate("2024-01-15T10:00:00Z"), + "trips_today": 5, + "rating": 4.8 +} +``` + +### Configuration + +```yaml +offline_store: + type: feast.infra.offline_stores.contrib.mongodb.mongodb_many.MongoDBOfflineStoreMany + connection_string: mongodb://localhost:27017 + database: feast +``` + +### When to Use + +✅ **Small to medium feature stores** — loads entire collection into memory +✅ **Fast PIT joins** — Ibis memtables are highly optimized +✅ **Simple schema** — flat documents, easy to query directly +✅ **Per-collection indexes** — each FV can have tailored indexes + +⚠️ **Caution**: Loads ALL documents from each collection. May OOM on very large collections. + +## MongoDBOfflineStoreOne (mongodb_one.py) + +**Single shared collection** — all FeatureViews store data in one collection with a discriminator field. + +### Schema + +```javascript +// Collection: feature_history (shared by all FVs) +{ + "entity_id": Binary("..."), // Serialized entity key + "feature_view": "driver_stats", // Discriminator + "features": { // Nested subdocument + "trips_today": 5, + "rating": 4.8 + }, + "event_timestamp": ISODate("2024-01-15T10:00:00Z"), + "created_at": ISODate("2024-01-15T10:00:01Z") +} +``` + +### Configuration + +```yaml +offline_store: + type: feast.infra.offline_stores.contrib.mongodb.mongodb_one.MongoDBOfflineStoreOne + connection_string: mongodb://localhost:27017 + database: feast + collection: feature_history +``` + +### When to Use + +✅ **Large feature stores** — filters by entity_id, doesn't load entire collection +✅ **Memory-safe** — processes in chunks, bounded memory usage +✅ **Schema consistency** — matches online store pattern +✅ **Efficient materialization** — MQL aggregation pipeline + +⚠️ **Trade-off**: Slightly slower than Many for small workloads due to serialization overhead. + +## Performance Comparison + +Benchmarks with 10 features, 3 historical rows per entity: + +| Entity Rows | Many (time) | One (time) | Winner | +|-------------|-------------|------------|--------| +| 1,000 | 0.30s | 0.06s | One | +| 10,000 | 0.20s | 0.31s | Many | +| 100,000 | 1.51s | 5.22s | Many | +| 1,000,000 | 16.08s | 212s | Many | + +### Memory Behavior + +| Scenario | Many | One | +|----------|------|-----| +| Large feature collection, small entity_df | ❌ Loads all | ✅ Filters | +| Small feature collection, large entity_df | ✅ Fast | ⚠️ Slower | + +## Choosing an Implementation + +``` + ┌─────────────────────────────┐ + │ Is your feature collection │ + │ larger than available RAM? │ + └─────────────────────────────┘ + │ + ┌──────────┴──────────┐ + ▼ ▼ + YES NO + │ │ + ▼ ▼ + ┌───────────────┐ ┌───────────────┐ + │ Use ONE │ │ Use MANY │ + │ (memory-safe) │ │ (faster) │ + └───────────────┘ └───────────────┘ +``` + +## Index Recommendations + +### Many (per-collection) + +```javascript +db.driver_stats.createIndex({ "driver_id": 1, "event_timestamp": -1 }) +``` + +### One (shared collection) + +```javascript +db.feature_history.createIndex({ + "entity_id": 1, + "feature_view": 1, + "event_timestamp": -1 +}) +``` + +The One implementation creates this index automatically on first use. + From 83d063beb889ecc2b4f214aa96b55229a860cfd6 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 14:25:44 -0400 Subject: [PATCH 21/76] Rename mongodb/ to mongodb_offline_store/, organize tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename module: mongodb/ → mongodb_offline_store/ (follows naming convention) - Move tests to mongodb_offline_store/ subdirectory: - test_mongodb_offline_retrieval.py → mongodb_offline_store/test_many.py - test_mongodb_offline_retrieval_native.py → mongodb_offline_store/test_one.py - benchmark_mongodb_offline_stores.py → mongodb_offline_store/benchmark.py - Update all imports to use mongodb_offline_store path Signed-off-by: Casey Clements --- .../contrib/{mongodb => mongodb_offline_store}/README.md | 4 ++-- .../contrib/{mongodb => mongodb_offline_store}/__init__.py | 0 .../{mongodb => mongodb_offline_store}/mongodb_many.py | 6 +++--- .../{mongodb => mongodb_offline_store}/mongodb_one.py | 6 ++---- .../contrib/mongodb_offline_store/__init__.py | 0 .../benchmark.py} | 4 ++-- .../test_many.py} | 2 +- .../test_one.py} | 2 +- 8 files changed, 11 insertions(+), 13 deletions(-) rename sdk/python/feast/infra/offline_stores/contrib/{mongodb => mongodb_offline_store}/README.md (95%) rename sdk/python/feast/infra/offline_stores/contrib/{mongodb => mongodb_offline_store}/__init__.py (100%) rename sdk/python/feast/infra/offline_stores/contrib/{mongodb => mongodb_offline_store}/mongodb_many.py (98%) rename sdk/python/feast/infra/offline_stores/contrib/{mongodb => mongodb_offline_store}/mongodb_one.py (99%) create mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/__init__.py rename sdk/python/tests/unit/infra/offline_stores/contrib/{benchmark_mongodb_offline_stores.py => mongodb_offline_store/benchmark.py} (99%) rename sdk/python/tests/unit/infra/offline_stores/contrib/{test_mongodb_offline_retrieval.py => mongodb_offline_store/test_many.py} (99%) rename sdk/python/tests/unit/infra/offline_stores/contrib/{test_mongodb_offline_retrieval_native.py => mongodb_offline_store/test_one.py} (99%) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb/README.md b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md similarity index 95% rename from sdk/python/feast/infra/offline_stores/contrib/mongodb/README.md rename to sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md index 44983940ff2..23afc6f2f55 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb/README.md +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md @@ -31,7 +31,7 @@ Two MongoDB offline store implementations optimized for different use cases. ```yaml offline_store: - type: feast.infra.offline_stores.contrib.mongodb.mongodb_many.MongoDBOfflineStoreMany + type: feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many.MongoDBOfflineStoreMany connection_string: mongodb://localhost:27017 database: feast ``` @@ -69,7 +69,7 @@ offline_store: ```yaml offline_store: - type: feast.infra.offline_stores.contrib.mongodb.mongodb_one.MongoDBOfflineStoreOne + type: feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one.MongoDBOfflineStoreOne connection_string: mongodb://localhost:27017 database: feast collection: feature_history diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb/__init__.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py similarity index 100% rename from sdk/python/feast/infra/offline_stores/contrib/mongodb/__init__.py rename to sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/__init__.py diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_many.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py similarity index 98% rename from sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_many.py rename to sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py index 7dac38af02b..5c5bf0b0ba1 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_many.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py @@ -34,7 +34,7 @@ SavedDatasetLocationAlreadyExists, ) from feast.feature_view import FeatureView -from feast.infra.offline_stores.contrib.mongodb import DRIVER_METADATA +from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA from feast.infra.offline_stores.ibis import ( get_historical_features_ibis, pull_all_from_table_or_query_ibis, @@ -214,7 +214,7 @@ def _to_proto_impl(self) -> DataSourceProto: data_source_proto = DataSourceProto( name=self.name, type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb.mongodb_many.MongoDBSourceMany", + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many.MongoDBSourceMany", field_mapping=self.field_mapping, custom_options=self._mongodb_options.to_proto(), description=self.description, @@ -322,7 +322,7 @@ def to_data_source(self) -> DataSource: class MongoDBOfflineStoreManyConfig(FeastConfigBaseModel): """Configuration for the MongoDB offline store (one collection per FeatureView).""" - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb.mongodb_many.MongoDBOfflineStoreMany" + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many.MongoDBOfflineStoreMany" """Offline store type selector""" connection_string: StrictStr = "mongodb://localhost:27017" diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py similarity index 99% rename from sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_one.py rename to sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index 293b785c868..f40f30df838 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -109,7 +109,7 @@ from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError from feast.feature_view import FeatureView from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb import DRIVER_METADATA +from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA from feast.infra.offline_stores.offline_store import ( OfflineStore, RetrievalJob, @@ -130,9 +130,7 @@ class MongoDBOfflineStoreOneConfig(FeastConfigBaseModel): """Configuration for the MongoDB offline store (single shared collection).""" - type: StrictStr = ( - "feast.infra.offline_stores.contrib.mongodb.mongodb_one.MongoDBOfflineStoreOne" - ) + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one.MongoDBOfflineStoreOne" """Offline store type selector""" connection_string: StrictStr = "mongodb://localhost:27017" diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/__init__.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py similarity index 99% rename from sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py rename to sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py index 3b663b150c7..f0d03e32e47 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/benchmark_mongodb_offline_stores.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py @@ -34,12 +34,12 @@ from feast import Entity, FeatureView, Field from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb.mongodb_many import ( +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( MongoDBOfflineStoreMany, MongoDBOfflineStoreManyConfig, MongoDBSourceMany, ) -from feast.infra.offline_stores.contrib.mongodb.mongodb_one import ( +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( MongoDBOfflineStoreOne, MongoDBOfflineStoreOneConfig, MongoDBSourceOne, diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py similarity index 99% rename from sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py rename to sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py index 1c9882900d5..cbd43ea8d18 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py @@ -19,7 +19,7 @@ from testcontainers.mongodb import MongoDbContainer from feast import Entity, FeatureView, Field -from feast.infra.offline_stores.contrib.mongodb.mongodb_many import ( +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( MongoDBOfflineStoreMany, MongoDBOfflineStoreManyConfig, MongoDBSourceMany, diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py similarity index 99% rename from sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py rename to sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py index f18d2d15af9..689fef915ee 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/test_mongodb_offline_retrieval_native.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py @@ -32,7 +32,7 @@ from feast import Entity, FeatureView, Field from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb.mongodb_one import ( +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( MongoDBOfflineStoreOne, MongoDBOfflineStoreOneConfig, MongoDBSourceOne, From 977c2409e6a7e9cfdb205899b8256facef3e0724 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 14:30:40 -0400 Subject: [PATCH 22/76] Update docstring in benchmark.py Signed-off-by: Casey Clements --- .../offline_stores/contrib/mongodb_offline_store/benchmark.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py index f0d03e32e47..49d8b8aeb69 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py @@ -1,5 +1,7 @@ """ -Performance benchmarks comparing Ibis vs Native MongoDB offline store implementations. +Performance benchmarks comparing the two MongoDB offline store implementations - +one Collection with all feature views +vs. a schema of N collections for N features views. These tests measure performance across different scaling dimensions: 1. Row count scaling (entity_df size) From 166d1510a0247749617cf2f122aa03c391ff4739 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 14:38:59 -0400 Subject: [PATCH 23/76] Update README to show created_at tie-breaker in Many schema Signed-off-by: Casey Clements --- .../offline_stores/contrib/mongodb_offline_store/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md index 23afc6f2f55..30ea64af2ac 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md @@ -22,11 +22,14 @@ Two MongoDB offline store implementations optimized for different use cases. { "driver_id": 1001, "event_timestamp": ISODate("2024-01-15T10:00:00Z"), + "created_at": ISODate("2024-01-15T10:00:01Z"), // Optional tie-breaker "trips_today": 5, "rating": 4.8 } ``` +Ties (same `event_timestamp`) are broken by `created_timestamp_column` if configured. + ### Configuration ```yaml From fe24361b82bd86daf4155c9ca5d683dc9ac09fe5 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 14:43:44 -0400 Subject: [PATCH 24/76] Update README index recommendations for Many implementation - Clarify that indexes should be on join keys + timestamp - Show example for compound join keys - Note that Many does not auto-create indexes Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md index 30ea64af2ac..561eb70ed76 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md @@ -128,16 +128,31 @@ Benchmarks with 10 features, 3 historical rows per entity: ### Many (per-collection) +Each collection should have an index on the join keys + timestamp: + ```javascript -db.driver_stats.createIndex({ "driver_id": 1, "event_timestamp": -1 }) +// For a FeatureView with join key "driver_id" +db.driver_stats.createIndex({ + "driver_id": 1, // Join key(s) + "event_timestamp": -1 +}) + +// For a FeatureView with compound join keys +db.order_stats.createIndex({ + "customer_id": 1, + "order_id": 1, + "event_timestamp": -1 +}) ``` +**Note**: The Many implementation does not auto-create indexes. Create them manually or via a migration script. + ### One (shared collection) ```javascript db.feature_history.createIndex({ "entity_id": 1, - "feature_view": 1, + "feature_view": 1, "event_timestamp": -1 }) ``` From 398110c8746593c92c465785d35ae39209c510ef Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 14:47:36 -0400 Subject: [PATCH 25/76] Add auto-create index to MongoDBOfflineStoreMany - Add _ensure_index_many() function with module-level cache - Call during pull_latest_from_table_or_query (materialization) - Creates index on join_keys + timestamp + created_timestamp - Checks for existing index before creating - Update README to reflect auto-create behavior Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/README.md | 2 +- .../mongodb_offline_store/mongodb_many.py | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md index 561eb70ed76..db6318ee17d 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md @@ -145,7 +145,7 @@ db.order_stats.createIndex({ }) ``` -**Note**: The Many implementation does not auto-create indexes. Create them manually or via a migration script. +**Note**: The Many implementation auto-creates indexes during `pull_latest_from_table_or_query` (materialization). ### One (shared collection) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py index 5c5bf0b0ba1..3faec603a38 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py @@ -359,6 +359,26 @@ def pull_latest_from_table_or_query( "MongoDB offline store is in preview. API may change without notice.", RuntimeWarning, ) + + # Ensure index exists for efficient queries + if MongoClient is not None: + connection_string = config.offline_store.connection_string + db_name = data_source.database or config.offline_store.database + client: Any = MongoClient( + connection_string, driver=DRIVER_METADATA, tz_aware=True + ) + try: + _ensure_index_many( + client=client, + db_name=db_name, + collection_name=data_source.collection, + join_keys=join_key_columns, + timestamp_field=timestamp_field, + created_timestamp_column=created_timestamp_column, + ) + finally: + client.close() + return pull_latest_from_table_or_query_ibis( config=config, data_source=data_source, @@ -475,6 +495,47 @@ def reader(data_source: DataSource, repo_path: str) -> Table: return reader +# Track which collections have had indexes ensured (module-level cache) +_indexes_ensured: set = set() + + +def _ensure_index_many( + client: Any, + db_name: str, + collection_name: str, + join_keys: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str] = None, +) -> None: + """Create recommended index on a Many-schema collection. + + Index is on: join_keys (ascending) + timestamp (descending) + created_at (descending). + Uses a module-level cache to avoid redundant index creation checks. + """ + cache_key = f"{db_name}.{collection_name}" + if cache_key in _indexes_ensured: + return + + coll = client[db_name][collection_name] + + # Build index key: join_keys (asc) + timestamp (desc) + created_at (desc) + index_keys = [(k, 1) for k in join_keys] + index_keys.append((timestamp_field, -1)) + if created_timestamp_column: + index_keys.append((created_timestamp_column, -1)) + + # Check if equivalent index already exists + existing_indexes = coll.index_information() + for idx_info in existing_indexes.values(): + if idx_info.get("key") == index_keys: + _indexes_ensured.add(cache_key) + return + + # Create the index + coll.create_index(index_keys, background=True) + _indexes_ensured.add(cache_key) + + def _build_data_source_writer( config: RepoConfig, ) -> Callable[[Table, DataSource, str, str, bool], None]: From 2feb97dde17568b264668c44c3676ca90b3e24e5 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 15:11:58 -0400 Subject: [PATCH 26/76] Update benchmark.py to use One/Many naming convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename functions: _generate_ibis_data → _generate_many_data, etc. - Rename fixtures: ibis_config → many_config, native_config → one_config - Rename tests: test_scale_rows_ibis → test_scale_rows_many, etc. - Update all docstrings and print statements - Update summary comparison output format Signed-off-by: Casey Clements --- .../mongodb_offline_store/benchmark.py | 162 +++++++++--------- 1 file changed, 82 insertions(+), 80 deletions(-) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py index 49d8b8aeb69..fa7f99e06bf 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py @@ -1,7 +1,8 @@ """ -Performance benchmarks comparing the two MongoDB offline store implementations - -one Collection with all feature views -vs. a schema of N collections for N features views. +Performance benchmarks comparing MongoDB offline store implementations: Many vs One. + +- Many: One collection per FeatureView (MongoDBOfflineStoreMany) +- One: Single shared collection for all FeatureViews (MongoDBOfflineStoreOne) These tests measure performance across different scaling dimensions: 1. Row count scaling (entity_df size) @@ -13,8 +14,8 @@ - Memory (peak Python memory via tracemalloc) - MongoDB server metrics (opcounters, execution stats) -Run with: pytest benchmark_mongodb_offline_stores.py -v -s -Skip slow tests: pytest benchmark_mongodb_offline_stores.py -v -s -m "not slow" +Run with: pytest benchmark.py -v -s +Skip slow tests: pytest benchmark.py -v -s -m "not slow" """ import time @@ -145,8 +146,8 @@ def mongodb_connection_string(mongodb_container: MongoDbContainer) -> str: @pytest.fixture -def ibis_config(mongodb_connection_string: str) -> RepoConfig: - """RepoConfig for Ibis implementation.""" +def many_config(mongodb_connection_string: str) -> RepoConfig: + """RepoConfig for Many implementation (one collection per FeatureView).""" return RepoConfig( project="benchmark", registry="memory://", @@ -161,8 +162,8 @@ def ibis_config(mongodb_connection_string: str) -> RepoConfig: @pytest.fixture -def native_config(mongodb_connection_string: str) -> RepoConfig: - """RepoConfig for Native implementation.""" +def one_config(mongodb_connection_string: str) -> RepoConfig: + """RepoConfig for One implementation (single shared collection).""" return RepoConfig( project="benchmark", registry="memory://", @@ -177,7 +178,7 @@ def native_config(mongodb_connection_string: str) -> RepoConfig: ) -def _generate_ibis_data( +def _generate_many_data( client: MongoClient, db_name: str, collection_name: str, @@ -185,7 +186,7 @@ def _generate_ibis_data( num_features: int, rows_per_entity: int = 5, ) -> datetime: - """Generate test data for Ibis (one collection per FV, flat schema).""" + """Generate test data for Many (one collection per FV, flat schema).""" collection = client[db_name][collection_name] collection.drop() @@ -206,7 +207,7 @@ def _generate_ibis_data( return now -def _generate_native_data( +def _generate_one_data( client: MongoClient, db_name: str, collection_name: str, @@ -215,7 +216,7 @@ def _generate_native_data( num_features: int, rows_per_entity: int = 5, ) -> datetime: - """Generate test data for Native (single collection, nested features).""" + """Generate test data for One (single collection, nested features).""" collection = client[db_name][collection_name] # Don't drop - may have multiple FVs in same collection @@ -241,8 +242,8 @@ def _generate_native_data( return now -def _create_ibis_fv(num_features: int) -> tuple: - """Create Ibis source and FeatureView.""" +def _create_many_fv(num_features: int) -> tuple: + """Create Many source and FeatureView.""" source = MongoDBSourceMany( name="driver_benchmark", database="benchmark_db", @@ -267,8 +268,8 @@ def _create_ibis_fv(num_features: int) -> tuple: return source, fv -def _create_native_fv(num_features: int) -> tuple: - """Create Native source and FeatureView.""" +def _create_one_fv(num_features: int) -> tuple: + """Create One source and FeatureView.""" source = MongoDBSourceOne( name="driver_benchmark", timestamp_field="event_timestamp", @@ -375,10 +376,10 @@ def _print_benchmark_result( @_requires_docker @pytest.mark.parametrize("num_rows", ROW_COUNTS) -def test_scale_rows_ibis( - mongodb_connection_string: str, ibis_config: RepoConfig, num_rows: int +def test_scale_rows_many( + mongodb_connection_string: str, many_config: RepoConfig, num_rows: int ) -> None: - """Benchmark Ibis implementation with varying entity_df sizes. + """Benchmark Many implementation with varying entity_df sizes. Measures: runtime, peak memory, MongoDB opcounters. """ @@ -387,7 +388,7 @@ def test_scale_rows_ibis( client = MongoClient(mongodb_connection_string) try: - now = _generate_ibis_data( + now = _generate_many_data( client, "benchmark_db", "driver_benchmark", @@ -396,7 +397,7 @@ def test_scale_rows_ibis( rows_per_entity=3, ) - _, fv = _create_ibis_fv(num_features) + _, fv = _create_many_fv(num_features) entity_df = pd.DataFrame( { @@ -409,7 +410,7 @@ def test_scale_rows_ibis( def run_query(): job = MongoDBOfflineStoreMany.get_historical_features( - config=ibis_config, + config=many_config, feature_views=[fv], feature_refs=feature_refs, entity_df=entity_df, @@ -428,10 +429,10 @@ def run_query(): @_requires_docker @pytest.mark.parametrize("num_rows", ROW_COUNTS) -def test_scale_rows_native( - mongodb_connection_string: str, native_config: RepoConfig, num_rows: int +def test_scale_rows_one( + mongodb_connection_string: str, one_config: RepoConfig, num_rows: int ) -> None: - """Benchmark Native implementation with varying entity_df sizes. + """Benchmark One implementation with varying entity_df sizes. Measures: runtime, peak memory, MongoDB opcounters. """ @@ -441,7 +442,7 @@ def test_scale_rows_native( client = MongoClient(mongodb_connection_string) try: client["benchmark_db"]["feature_history"].drop() - now = _generate_native_data( + now = _generate_one_data( client, "benchmark_db", "feature_history", @@ -451,7 +452,7 @@ def test_scale_rows_native( rows_per_entity=3, ) - _, fv = _create_native_fv(num_features) + _, fv = _create_one_fv(num_features) entity_df = pd.DataFrame( { @@ -464,7 +465,7 @@ def test_scale_rows_native( def run_query(): job = MongoDBOfflineStoreOne.get_historical_features( - config=native_config, + config=one_config, feature_views=[fv], feature_refs=feature_refs, entity_df=entity_df, @@ -490,15 +491,15 @@ def run_query(): @_requires_docker @pytest.mark.parametrize("num_features", FEATURE_COUNTS) -def test_wide_features_ibis( - mongodb_connection_string: str, ibis_config: RepoConfig, num_features: int +def test_wide_features_many( + mongodb_connection_string: str, many_config: RepoConfig, num_features: int ) -> None: - """Benchmark Ibis with varying feature width.""" + """Benchmark Many with varying feature width.""" num_entities = 1000 client = MongoClient(mongodb_connection_string) try: - now = _generate_ibis_data( + now = _generate_many_data( client, "benchmark_db", "driver_benchmark", @@ -507,7 +508,7 @@ def test_wide_features_ibis( rows_per_entity=3, ) - _, fv = _create_ibis_fv(num_features) + _, fv = _create_many_fv(num_features) entity_df = pd.DataFrame( { @@ -520,7 +521,7 @@ def test_wide_features_ibis( def run_query(): job = MongoDBOfflineStoreMany.get_historical_features( - config=ibis_config, + config=many_config, feature_views=[fv], feature_refs=feature_refs, entity_df=entity_df, @@ -541,16 +542,16 @@ def run_query(): @_requires_docker @pytest.mark.parametrize("num_features", FEATURE_COUNTS) -def test_wide_features_native( - mongodb_connection_string: str, native_config: RepoConfig, num_features: int +def test_wide_features_one( + mongodb_connection_string: str, one_config: RepoConfig, num_features: int ) -> None: - """Benchmark Native with varying feature width.""" + """Benchmark One with varying feature width.""" num_entities = 1000 client = MongoClient(mongodb_connection_string) try: client["benchmark_db"]["feature_history"].drop() - now = _generate_native_data( + now = _generate_one_data( client, "benchmark_db", "feature_history", @@ -560,7 +561,7 @@ def test_wide_features_native( rows_per_entity=3, ) - _, fv = _create_native_fv(num_features) + _, fv = _create_one_fv(num_features) entity_df = pd.DataFrame( { @@ -573,7 +574,7 @@ def test_wide_features_native( def run_query(): job = MongoDBOfflineStoreOne.get_historical_features( - config=native_config, + config=one_config, feature_views=[fv], feature_refs=feature_refs, entity_df=entity_df, @@ -599,10 +600,10 @@ def run_query(): @_requires_docker @pytest.mark.parametrize("unique_ratio", [1.0, 0.5, 0.1]) # 100%, 50%, 10% unique -def test_entity_skew_ibis( - mongodb_connection_string: str, ibis_config: RepoConfig, unique_ratio: float +def test_entity_skew_many( + mongodb_connection_string: str, many_config: RepoConfig, unique_ratio: float ) -> None: - """Benchmark Ibis with varying entity uniqueness in entity_df.""" + """Benchmark Many with varying entity uniqueness in entity_df.""" import numpy as np total_rows = 5000 @@ -612,7 +613,7 @@ def test_entity_skew_ibis( client = MongoClient(mongodb_connection_string) try: - now = _generate_ibis_data( + now = _generate_many_data( client, "benchmark_db", "driver_benchmark", @@ -621,7 +622,7 @@ def test_entity_skew_ibis( rows_per_entity=5, ) - _, fv = _create_ibis_fv(num_features) + _, fv = _create_many_fv(num_features) # Create entity_df with repeated entity_ids entity_ids = np.random.choice( @@ -640,7 +641,7 @@ def test_entity_skew_ibis( def run_query(): job = MongoDBOfflineStoreMany.get_historical_features( - config=ibis_config, + config=many_config, feature_views=[fv], feature_refs=feature_refs, entity_df=entity_df, @@ -652,7 +653,7 @@ def run_query(): result = _run_benchmark_full(run_query, mongo_client=client) print( - f"\n[IBIS] Unique ratio: {unique_ratio:.0%} ({num_unique_entities:,} unique / {total_rows:,} rows)" + f"\n[MANY] Unique ratio: {unique_ratio:.0%} ({num_unique_entities:,} unique / {total_rows:,} rows)" ) print(f" Time: {result.elapsed_seconds:.3f}s") print(f" Memory: {result.peak_memory_mb:.1f} MB") @@ -664,10 +665,10 @@ def run_query(): @_requires_docker @pytest.mark.parametrize("unique_ratio", [1.0, 0.5, 0.1]) -def test_entity_skew_native( - mongodb_connection_string: str, native_config: RepoConfig, unique_ratio: float +def test_entity_skew_one( + mongodb_connection_string: str, one_config: RepoConfig, unique_ratio: float ) -> None: - """Benchmark Native with varying entity uniqueness in entity_df.""" + """Benchmark One with varying entity uniqueness in entity_df.""" import numpy as np total_rows = 5000 @@ -678,7 +679,7 @@ def test_entity_skew_native( client = MongoClient(mongodb_connection_string) try: client["benchmark_db"]["feature_history"].drop() - now = _generate_native_data( + now = _generate_one_data( client, "benchmark_db", "feature_history", @@ -688,7 +689,7 @@ def test_entity_skew_native( rows_per_entity=5, ) - _, fv = _create_native_fv(num_features) + _, fv = _create_one_fv(num_features) entity_ids = np.random.choice( num_unique_entities, size=total_rows, replace=True @@ -706,7 +707,7 @@ def test_entity_skew_native( def run_query(): job = MongoDBOfflineStoreOne.get_historical_features( - config=native_config, + config=one_config, feature_views=[fv], feature_refs=feature_refs, entity_df=entity_df, @@ -718,7 +719,7 @@ def run_query(): result = _run_benchmark_full(run_query, mongo_client=client) print( - f"\n[NATIVE] Unique ratio: {unique_ratio:.0%} ({num_unique_entities:,} unique / {total_rows:,} rows)" + f"\n[ONE] Unique ratio: {unique_ratio:.0%} ({num_unique_entities:,} unique / {total_rows:,} rows)" ) print(f" Time: {result.elapsed_seconds:.3f}s") print(f" Memory: {result.peak_memory_mb:.1f} MB") @@ -735,7 +736,7 @@ def run_query(): @_requires_docker def test_summary_comparison( - mongodb_connection_string: str, ibis_config: RepoConfig, native_config: RepoConfig + mongodb_connection_string: str, many_config: RepoConfig, one_config: RepoConfig ) -> None: """Run a standard comparison and print summary with full metrics.""" num_entities = 2000 @@ -743,8 +744,8 @@ def test_summary_comparison( client = MongoClient(mongodb_connection_string) try: - # Setup Ibis data - now = _generate_ibis_data( + # Setup Many data + now = _generate_many_data( client, "benchmark_db", "driver_benchmark", @@ -753,9 +754,9 @@ def test_summary_comparison( rows_per_entity=5, ) - # Setup Native data + # Setup One data client["benchmark_db"]["feature_history"].drop() - _generate_native_data( + _generate_one_data( client, "benchmark_db", "feature_history", @@ -774,13 +775,13 @@ def test_summary_comparison( feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] - # Ibis benchmark - _, ibis_fv = _create_ibis_fv(num_features) + # Many benchmark + _, many_fv = _create_many_fv(num_features) - def run_ibis(): + def run_many(): job = MongoDBOfflineStoreMany.get_historical_features( - config=ibis_config, - feature_views=[ibis_fv], + config=many_config, + feature_views=[many_fv], feature_refs=feature_refs, entity_df=entity_df, registry=MagicMock(), @@ -789,15 +790,15 @@ def run_ibis(): ) return job.to_df() - ibis_result = _run_benchmark_full(run_ibis, mongo_client=client) + many_result = _run_benchmark_full(run_many, mongo_client=client) - # Native benchmark - _, native_fv = _create_native_fv(num_features) + # One benchmark + _, one_fv = _create_one_fv(num_features) - def run_native(): + def run_one(): job = MongoDBOfflineStoreOne.get_historical_features( - config=native_config, - feature_views=[native_fv], + config=one_config, + feature_views=[one_fv], feature_refs=feature_refs, entity_df=entity_df, registry=MagicMock(), @@ -806,30 +807,31 @@ def run_native(): ) return job.to_df() - native_result = _run_benchmark_full(run_native, mongo_client=client) + one_result = _run_benchmark_full(run_one, mongo_client=client) # Print summary print("\n" + "=" * 70) - print("SUMMARY COMPARISON") + print("SUMMARY COMPARISON: Many vs One") print("=" * 70) print(f"Entities: {num_entities:,} | Features: {num_features}") print("-" * 70) - print(f"{'Metric':<20} {'Ibis':>20} {'Native':>20}") + print(f"{'Metric':<20} {'Many':>20} {'One':>20}") print("-" * 70) print( - f"{'Time (s)':<20} {ibis_result.elapsed_seconds:>20.3f} {native_result.elapsed_seconds:>20.3f}" + f"{'Time (s)':<20} {many_result.elapsed_seconds:>20.3f} {one_result.elapsed_seconds:>20.3f}" ) print( - f"{'Memory (MB)':<20} {ibis_result.peak_memory_mb:>20.1f} {native_result.peak_memory_mb:>20.1f}" + f"{'Memory (MB)':<20} {many_result.peak_memory_mb:>20.1f} {one_result.peak_memory_mb:>20.1f}" ) print( - f"{'Rows/sec':<20} {num_entities / ibis_result.elapsed_seconds:>20,.0f} {num_entities / native_result.elapsed_seconds:>20,.0f}" + f"{'Rows/sec':<20} {num_entities / many_result.elapsed_seconds:>20,.0f} {num_entities / one_result.elapsed_seconds:>20,.0f}" ) print("-" * 70) - if native_result.elapsed_seconds > 0: - ratio = native_result.elapsed_seconds / ibis_result.elapsed_seconds - print(f"Ibis is {ratio:.1f}x faster than Native") + if one_result.elapsed_seconds > 0: + ratio = one_result.elapsed_seconds / many_result.elapsed_seconds + faster = "Many" if ratio > 1 else "One" + print(f"{faster} is {max(ratio, 1 / ratio):.1f}x faster") print("=" * 70) finally: From 96243110ff6ab1085c10ff976c40426871824e45 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 15:15:54 -0400 Subject: [PATCH 27/76] Add comprehensive module docstring to mongodb_many.py Documents: - Collection structure (one per FeatureView) - Index creation (auto-created during materialization) - Document schema (flat, top-level features) - Point-in-time join strategy (Ibis memtables) - Performance characteristics and memory considerations - When to use vs MongoDBOfflineStoreOne - Comparison table with One implementation Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_many.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py index 3faec603a38..2fdf67200d1 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py @@ -12,6 +12,69 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +MongoDB Offline Store Implementation (Many Collections). + +This module implements a MongoDB offline store using a many-collection schema +where each FeatureView maps to its own dedicated MongoDB collection. It uses +Ibis for point-in-time joins, loading collection data into in-memory tables. + +Collection Structure: + Each FeatureView has its own collection named after the source: + - driver_stats FeatureView → db.driver_stats collection + - vehicle_stats FeatureView → db.vehicle_stats collection + +Collection Index (auto-created during materialization): + db..createIndex({ + "": 1, + "": 1, // if compound key + "event_timestamp": -1, + "created_at": -1 // if created_timestamp_column is set + }) + +Document Schema (example for driver_stats): + { + "_id": ObjectId(), + "driver_id": 1001, + "event_timestamp": ISODate("2026-01-20T12:00:00Z"), + "created_at": ISODate("2026-01-20T12:00:05Z"), + "rating": 4.91, + "trips_last_7d": 132 + } + + Note: Features are stored as top-level fields (flat schema), not nested + in a subdocument. This differs from the "One" implementation. + +Point-in-Time Join Strategy: + 1. Load entire collection into an Ibis memtable + 2. Load entity_df into an Ibis memtable + 3. Use Ibis/pandas merge_asof for point-in-time correctness + 4. Apply TTL filtering per FeatureView + +Performance Characteristics: + - Fast for small to medium collections (fits in memory) + - Optimized Ibis memtable operations for joins + - ⚠️ Loads ENTIRE collection into memory - may OOM on large collections + +When to Use: + ✅ Small to medium feature stores where collections fit in memory + ✅ When query performance is the priority + ✅ When you want simple, flat document schemas + ✅ When each FeatureView has independent scaling needs + + ❌ Avoid when collections are very large (use MongoDBOfflineStoreOne instead) + ❌ Avoid in memory-constrained environments + +Comparison with MongoDBOfflineStoreOne: + | Aspect | Many (this module) | One | + |-----------------|----------------------|------------------------| + | Collections | N (one per FV) | 1 (shared) | + | Schema | Flat top-level | Nested features{} | + | Memory | Loads all docs | Filters by entity | + | Performance | Faster at scale | Memory-efficient | + | Entity ID | Native columns | Serialized bytes | +""" + import json import warnings from datetime import datetime From 5c93a5126cbab6d0a39e5af672540551fe428f81 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 15:20:02 -0400 Subject: [PATCH 28/76] Add Feature Freshness and Schema Evolution docs to mongodb_many.py Add missing documentation sections: - Feature Freshness Semantics: document-level freshness, not per-feature - Schema Evolution ('Feature Creep'): flexible schema implications - Notes: entity keys as native types, PIT correctness, TTL constraints Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_many.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py index 2fdf67200d1..b1112552f39 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py @@ -45,6 +45,43 @@ Note: Features are stored as top-level fields (flat schema), not nested in a subdocument. This differs from the "One" implementation. +Feature Freshness Semantics: + This implementation operates at *document-level freshness*, not + per-feature freshness. During retrieval (e.g. point-in-time joins), + the system selects the most recent document for a given entity that + satisfies time constraints, and then extracts all requested features + from that document. + + As a result, if a newer document contains only a subset of features, + missing features will be returned as NULL—even if older documents + contained values for those features. The system does not backfill + individual feature values from earlier events. + + This behavior matches common Feast offline store semantics, but may + differ from systems that compute "latest value per feature". + +Schema Evolution ("Feature Creep"): + Because documents can have varying fields over time, different documents + in the same collection may contain different sets of feature fields. + This supports: + - Adding new features without backfilling historical data + - Partial writes or sparse feature computation + + However, it also implies: + - Newly added features will be NULL for older events + - Partially populated documents may lead to NULL values even + when older data contained those features + + Users should ensure that feature computation pipelines write complete + feature sets when consistent availability is required. + +Notes: + - Entity keys are stored as native MongoDB types (not serialized), + which differs from the "One" implementation. + - Point-in-time correctness is enforced per FeatureView. + - TTL (time-to-live) constraints are applied per FeatureView during + historical retrieval. + Point-in-Time Join Strategy: 1. Load entire collection into an Ibis memtable 2. Load entity_df into an Ibis memtable From fd70c13fcda2e39edcd842d66a7f2206722f9879 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 16:27:35 -0400 Subject: [PATCH 29/76] Add MongoDB DataSourceCreators for universal Feast tests Add DataSourceCreator implementations for MongoDB offline stores: - MongoDBManyDataSourceCreator: Fully functional, passes universal tests. Creates one collection per FeatureView with flat document schema. - MongoDBOneDataSourceCreator: Implementation exists but NOT registered. The One schema requires knowing join keys vs features at data creation time, but DataSourceCreator.create_data_source() doesn't receive entity definitions. See TODO in mongodb.py for details on required interface changes. Other changes: - Fix data_source_class_type path in mongodb_one.py (mongodb_native -> mongodb_one) - Improve datetime handling in mongodb_one.py for non-datetime columns - Add 'mongodb' marker to pytest.ini - Register MongoDBManyDataSourceCreator in repo_configuration.py Signed-off-by: Casey Clements --- .secrets.baseline | 6 +- .../mongodb_offline_store/mongodb_one.py | 9 +- sdk/python/pytest.ini | 1 + .../feature_repos/repo_configuration.py | 27 ++ .../universal/data_sources/mongodb.py | 316 ++++++++++++++++++ 5 files changed, 354 insertions(+), 5 deletions(-) create mode 100644 sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py diff --git a/.secrets.baseline b/.secrets.baseline index 510ca5cad38..6a6159b3455 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1460,14 +1460,14 @@ "filename": "sdk/python/tests/universal/feature_repos/repo_configuration.py", "hashed_secret": "d90e76ef629fb00c95f4e84fec29fbda111e2392", "is_verified": false, - "line_number": 459 + "line_number": 479 }, { "type": "Secret Keyword", "filename": "sdk/python/tests/universal/feature_repos/repo_configuration.py", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 461 + "line_number": 481 } ], "sdk/python/tests/universal/feature_repos/universal/data_sources/file.py": [ @@ -1539,5 +1539,5 @@ } ] }, - "generated_at": "2026-03-18T13:51:43Z" + "generated_at": "2026-03-20T20:27:19Z" } diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index f40f30df838..3c1aeaf7081 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -219,7 +219,7 @@ def _to_proto_impl(self) -> DataSourceProto: return DataSourceProto( name=self.name, type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBSourceOne", + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one.MongoDBSourceOne", field_mapping=self.field_mapping, custom_options=DataSourceProto.CustomSourceOptions( configuration=json.dumps({"feature_view": self.name}).encode() @@ -655,7 +655,12 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: """ # Prepare entity_df: ensure timestamps are UTC result = entity_subset_df.copy() - if result[event_timestamp_col].dt.tz is None: + # Convert timestamp column to datetime if needed + if not pd.api.types.is_datetime64_any_dtype(result[event_timestamp_col]): + result[event_timestamp_col] = pd.to_datetime( + result[event_timestamp_col], utc=True + ) + elif result[event_timestamp_col].dt.tz is None: result[event_timestamp_col] = pd.to_datetime( result[event_timestamp_col], utc=True ) diff --git a/sdk/python/pytest.ini b/sdk/python/pytest.ini index 1ad76b978e4..d5ad19660b7 100644 --- a/sdk/python/pytest.ini +++ b/sdk/python/pytest.ini @@ -21,6 +21,7 @@ markers = cloud: Tests requiring cloud credentials local_only: Tests that run entirely locally xdist_group: Group tests to run in the same xdist worker + mongodb: Tests requiring MongoDB timeout = 300 timeout_method = thread diff --git a/sdk/python/tests/universal/feature_repos/repo_configuration.py b/sdk/python/tests/universal/feature_repos/repo_configuration.py index ddd952f71dc..2033d416032 100644 --- a/sdk/python/tests/universal/feature_repos/repo_configuration.py +++ b/sdk/python/tests/universal/feature_repos/repo_configuration.py @@ -108,6 +108,33 @@ ] ) +# MongoDB offline stores (require testcontainers and pymongo) +if os.getenv("FEAST_LOCAL_ONLINE_CONTAINER", "False") == "True": + try: + from tests.universal.feature_repos.universal.data_sources.mongodb import ( + MongoDBManyDataSourceCreator, + # MongoDBOneDataSourceCreator, # TODO: Not registered - see TODO in mongodb.py + ) + + AVAILABLE_OFFLINE_STORES.extend( + [ + ("local", MongoDBManyDataSourceCreator), + # TODO: MongoDBOneDataSourceCreator requires DataSourceCreator interface + # changes to pass entity/join key info. See mongodb.py for details. + # ("local", MongoDBOneDataSourceCreator), + ] + ) + OFFLINE_STORE_TO_PROVIDER_CONFIG["mongodb_many"] = ( + "local", + MongoDBManyDataSourceCreator, + ) + # OFFLINE_STORE_TO_PROVIDER_CONFIG["mongodb_one"] = ( + # "local", + # MongoDBOneDataSourceCreator, + # ) + except ImportError: + pass # pymongo or testcontainers not installed + AVAILABLE_ONLINE_STORES: Dict[ str, Tuple[Union[str, Dict[Any, Any]], Optional[Type[OnlineStoreCreator]]] ] = {"sqlite": ({"type": "sqlite"}, None)} diff --git a/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py b/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py new file mode 100644 index 00000000000..8eedc3b6957 --- /dev/null +++ b/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py @@ -0,0 +1,316 @@ +""" +MongoDB DataSourceCreator implementations for universal Feast tests. + +Provides two implementations matching the two offline store schemas: +- MongoDBManyDataSourceCreator: One collection per FeatureView (Many) +- MongoDBOneDataSourceCreator: Single shared collection (One) +""" + +from typing import Any, Dict, Optional + +import pandas as pd +import pytest +from testcontainers.mongodb import MongoDbContainer + +from feast.data_source import DataSource +from feast.feature_logging import LoggingDestination +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( + MongoDBOfflineStoreManyConfig, + MongoDBSourceMany, + SavedDatasetMongoDBStorageMany, +) +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( + MongoDBOfflineStoreOneConfig, + MongoDBSourceOne, +) +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import FeastConfigBaseModel +from feast.saved_dataset import SavedDatasetStorage +from tests.universal.feature_repos.universal.data_source_creator import ( + DataSourceCreator, +) + +# Import pymongo - will be available since we're testing MongoDB +try: + from pymongo import MongoClient +except ImportError: + MongoClient = None # type: ignore + + +class MongoDBManyDataSourceCreator(DataSourceCreator): + """DataSourceCreator for MongoDBOfflineStoreMany (one collection per FeatureView).""" + + def __init__(self, project_name: str, *args, **kwargs): + super().__init__(project_name) + self.container = MongoDbContainer( + "mongo:7.0", + username="test", + password="test", # pragma: allowlist secret + ).with_exposed_ports(27017) + self.container.start() + self.port = self.container.get_exposed_port(27017) + self.connection_string = ( + f"mongodb://test:test@localhost:{self.port}" # pragma: allowlist secret + ) + self.database = f"feast_test_{project_name}" + self.collections_created: list[str] = [] + + def create_data_source( + self, + df: pd.DataFrame, + destination_name: str, + created_timestamp_column: str = "created_ts", + field_mapping: Optional[Dict[str, str]] = None, + timestamp_field: Optional[str] = "ts", + ) -> DataSource: + """Create a MongoDB data source by inserting df into a collection.""" + collection_name = self.get_prefixed_table_name(destination_name) + + # Insert data into MongoDB + client: Any = MongoClient(self.connection_string, tz_aware=True) + try: + coll = client[self.database][collection_name] + coll.drop() # Clean slate + records = df.to_dict("records") + if records: + coll.insert_many(records) + self.collections_created.append(collection_name) + finally: + client.close() + + return MongoDBSourceMany( + name=destination_name, + database=self.database, + collection=collection_name, + timestamp_field=timestamp_field or "ts", + created_timestamp_column=created_timestamp_column, + field_mapping=field_mapping, + ) + + def get_prefixed_table_name(self, suffix: str) -> str: + return f"{self.project_name}_{suffix}" + + def create_offline_store_config(self) -> FeastConfigBaseModel: + return MongoDBOfflineStoreManyConfig( + connection_string=self.connection_string, + database=self.database, + ) + + def create_saved_dataset_destination(self) -> SavedDatasetStorage: + return SavedDatasetMongoDBStorageMany( + database=self.database, + collection=f"{self.project_name}_saved_dataset", + ) + + def create_logged_features_destination(self) -> LoggingDestination: + # MongoDB doesn't have a native LoggingDestination yet + # Return None or raise NotImplementedError for now + raise NotImplementedError( + "MongoDB LoggingDestination not implemented. " + "Tests requiring logging features will be skipped." + ) + + def teardown(self): + """Clean up: drop collections and stop container.""" + try: + client: Any = MongoClient(self.connection_string, tz_aware=True) + try: + db = client[self.database] + for coll_name in self.collections_created: + db[coll_name].drop() + finally: + client.close() + except Exception: + pass # Container may already be stopped + self.container.stop() + + @staticmethod + def test_markers() -> list: + """Mark tests as requiring MongoDB.""" + return [pytest.mark.mongodb] + + +class MongoDBOneDataSourceCreator(DataSourceCreator): + """DataSourceCreator for MongoDBOfflineStoreOne (single shared collection). + + This implementation uses the nested features schema where all FeatureViews + share a single collection with a discriminator field. + + TODO: This DataSourceCreator has a fundamental limitation. The One schema + requires knowing which columns are join keys vs features to properly + serialize entity_id and nest features. However, create_data_source() only + receives a DataFrame and column names - it doesn't have access to Entity + definitions that specify join keys. + + Current workaround uses heuristics (columns ending in '_id' with int/string + dtype), which is fragile. A proper fix would require modifying the + DataSourceCreator interface to pass entity/join key information to + create_data_source(), which is a Feast core change. + + For now, universal tests may fail for FeatureViews where the heuristic + doesn't correctly identify join keys. Use unit tests in + tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py + for comprehensive testing of the One implementation. + """ + + ENTITY_KEY_VERSION = 3 + + def __init__(self, project_name: str, *args, **kwargs): + super().__init__(project_name) + self.container = MongoDbContainer( + "mongo:7.0", + username="test", + password="test", # pragma: allowlist secret + ).with_exposed_ports(27017) + self.container.start() + self.port = self.container.get_exposed_port(27017) + self.connection_string = ( + f"mongodb://test:test@localhost:{self.port}" # pragma: allowlist secret + ) + self.database = f"feast_test_{project_name}" + self.collection = "feature_history" + self.feature_views_created: list[str] = [] + # Track entity key columns per feature view for serialization + self._entity_key_columns: Dict[str, list[str]] = {} + + def _serialize_entity_key(self, row: pd.Series, join_keys: list[str]) -> bytes: + """Serialize entity key columns to bytes.""" + entity_key = EntityKeyProto() + for key in join_keys: + entity_key.join_keys.append(key) + val = ValueProto() + value = row[key] + if isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + elif isinstance(value, float): + val.double_val = value + elif isinstance(value, bool): + val.bool_val = value + else: + val.string_val = str(value) + entity_key.entity_values.append(val) + return serialize_entity_key(entity_key, self.ENTITY_KEY_VERSION) + + def create_data_source( + self, + df: pd.DataFrame, + destination_name: str, + created_timestamp_column: str = "created_ts", + field_mapping: Optional[Dict[str, str]] = None, + timestamp_field: Optional[str] = "ts", + ) -> DataSource: + """Create a MongoDB data source by inserting df into the shared collection. + + The data is transformed into the One schema: + - entity_id: serialized entity key + - feature_view: destination_name + - features: nested dict of feature values + - event_timestamp: from timestamp_field + - created_at: from created_timestamp_column + """ + # Determine which columns are join keys vs features + # Join keys must be integer or string types (serializable as entity keys) + timestamp_cols = {timestamp_field, created_timestamp_column} + all_cols = set(df.columns) - timestamp_cols - {None} + + # Heuristic: identify join keys + # 1. Must end with "_id" or be a known key name + # 2. Must be integer or string type (not float) + join_keys = [] + for c in all_cols: + if c.endswith("_id") or c in {"driver", "customer", "entity"}: + dtype = df[c].dtype + # Only integer or string types can be join keys + if dtype in ("int64", "int32", "object") or str(dtype).startswith( + "int" + ): + join_keys.append(c) + + if not join_keys: + # Fallback: first integer column + for c in all_cols: + if df[c].dtype in ("int64", "int32") or str(df[c].dtype).startswith( + "int" + ): + join_keys = [c] + break + + feature_cols = [c for c in all_cols if c not in join_keys] + + # Store for later use + self._entity_key_columns[destination_name] = join_keys + + # Transform to One schema + docs = [] + for _, row in df.iterrows(): + entity_id = self._serialize_entity_key(row, join_keys) + features = {col: row[col] for col in feature_cols if pd.notna(row.get(col))} + + doc = { + "entity_id": entity_id, + "feature_view": destination_name, + "features": features, + } + if timestamp_field and timestamp_field in row: + doc["event_timestamp"] = row[timestamp_field] + if created_timestamp_column and created_timestamp_column in row: + doc["created_at"] = row[created_timestamp_column] + + docs.append(doc) + + # Insert into MongoDB + client: Any = MongoClient(self.connection_string, tz_aware=True) + try: + coll = client[self.database][self.collection] + if docs: + coll.insert_many(docs) + self.feature_views_created.append(destination_name) + finally: + client.close() + + return MongoDBSourceOne( + name=destination_name, + timestamp_field="event_timestamp", + created_timestamp_column="created_at" if created_timestamp_column else None, + field_mapping=field_mapping, + ) + + def get_prefixed_table_name(self, suffix: str) -> str: + return f"{self.project_name}_{suffix}" + + def create_offline_store_config(self) -> FeastConfigBaseModel: + return MongoDBOfflineStoreOneConfig( + connection_string=self.connection_string, + database=self.database, + collection=self.collection, + ) + + def create_saved_dataset_destination(self) -> SavedDatasetStorage: + # One implementation doesn't have SavedDatasetStorage yet + raise NotImplementedError( + "MongoDBOfflineStoreOne SavedDatasetStorage not implemented." + ) + + def create_logged_features_destination(self) -> LoggingDestination: + raise NotImplementedError("MongoDB LoggingDestination not implemented.") + + def teardown(self): + """Clean up: drop the collection and stop container.""" + try: + client: Any = MongoClient(self.connection_string, tz_aware=True) + try: + client[self.database][self.collection].drop() + finally: + client.close() + except Exception: + pass + self.container.stop() + + @staticmethod + def test_markers() -> list: + """Mark tests as requiring MongoDB.""" + return [pytest.mark.mongodb] From b4a0260994014828aa10b81a012edd8c2680da7d Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 20 Mar 2026 16:57:17 -0400 Subject: [PATCH 30/76] Add .secrets.baseline Signed-off-by: Casey Clements --- .secrets.baseline | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 6a6159b3455..ae02873f833 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1460,14 +1460,14 @@ "filename": "sdk/python/tests/universal/feature_repos/repo_configuration.py", "hashed_secret": "d90e76ef629fb00c95f4e84fec29fbda111e2392", "is_verified": false, - "line_number": 479 + "line_number": 486 }, { "type": "Secret Keyword", "filename": "sdk/python/tests/universal/feature_repos/repo_configuration.py", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 481 + "line_number": 488 } ], "sdk/python/tests/universal/feature_repos/universal/data_sources/file.py": [ @@ -1539,5 +1539,5 @@ } ] }, - "generated_at": "2026-03-20T20:27:19Z" + "generated_at": "2026-03-20T20:55:36Z" } From 9efd700ae7294f6c563a18d55972bca656edf077 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 14 Apr 2026 13:17:15 -0400 Subject: [PATCH 31/76] Addressed PR comment: join_keys = get_expected_join_keys(project, feature_views, registry) Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_one.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index 3c1aeaf7081..9aea8f2aeda 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -82,6 +82,7 @@ import json import warnings +from collections import defaultdict from datetime import datetime, timezone from typing import ( Any, @@ -116,6 +117,7 @@ RetrievalMetadata, ) from feast.infra.offline_stores.offline_utils import ( + get_expected_join_keys, infer_event_timestamp_from_entity_df, ) from feast.infra.registry.base_registry import BaseRegistry @@ -626,10 +628,10 @@ def get_historical_features( event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) # Map "feature_view:feature" refs → {fv_name: [feature, ...]} - fv_to_features: Dict[str, List[str]] = {} + fv_to_features: Dict[str, List[str]] = defaultdict(list) for ref in feature_refs: fv_name, feat_name = ref.split(":", 1) - fv_to_features.setdefault(fv_name, []).append(feat_name) + fv_to_features[fv_name].append(feat_name) fv_names = list(fv_to_features.keys()) fv_by_name = {fv.name: fv for fv in feature_views} @@ -653,7 +655,10 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: entity_subset_df: Chunk of entity DataFrame to process coll: MongoDB collection object (reused across chunks) """ - # Prepare entity_df: ensure timestamps are UTC + # Copy is required: iloc yields a view, and we both normalise the + # timestamp column in-place and add a temporary _entity_id column. + # Modifying the view directly would corrupt working_df and raise + # SettingWithCopyWarning. result = entity_subset_df.copy() # Convert timestamp column to datetime if needed if not pd.api.types.is_datetime64_any_dtype(result[event_timestamp_col]): @@ -665,17 +670,15 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: result[event_timestamp_col], utc=True ) - # Get join keys (all columns except event_timestamp and internal columns) - entity_columns = [ - c - for c in result.columns - if c != event_timestamp_col and not c.startswith("_") - ] + # Resolve join keys from the registry — the authoritative source. + # Do NOT derive from chunk columns: the entity_df may carry extra + # columns (labels, metadata) that must not enter the entity key. + join_keys = get_expected_join_keys(project, feature_views, registry) # Serialize entity keys to bytes (same format as online store) result["_entity_id"] = result.apply( lambda row: _serialize_entity_key_from_row( - row, entity_columns, entity_key_version + row, join_keys, entity_key_version ), axis=1, ) @@ -778,6 +781,9 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: def _run() -> pyarrow.Table: # Add row index to preserve original ordering + # Copy is required: we write _row_idx into this DataFrame to + # restore original ordering after chunk results are concatenated, + # and must not mutate the caller's entity_df. working_df = entity_df.copy() working_df["_row_idx"] = range(len(working_df)) From 5d67b3fc01b84e7978d5efe8fafb5d4ae1ce5d52 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 14 Apr 2026 13:29:43 -0400 Subject: [PATCH 32/76] Adds tests scenario that not using offline_utils.get_expected_join_keys would get wrong. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/test_one.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py index 689fef915ee..f85531c5233 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py @@ -607,3 +607,72 @@ def test_compound_join_keys( assert result_df.loc[1, "app_opens"] == 55 # user 2, tablet assert result_df.loc[2, "app_opens"] == 25 + + +@_requires_docker +def test_entity_df_with_extra_columns( + repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView +) -> None: + """Extra columns in entity_df (e.g. labels) must not corrupt entity key serialization. + + A real training entity_df carries label columns alongside the join key and + timestamp. Before the fix, ``_run_single`` derived entity columns from ALL + non-timestamp DataFrame columns, so a label column like ``trip_success`` + would be included in the serialized entity key. The resulting key would + not match any document in MongoDB and every feature would silently come + back as ``None``. + + This test pins that contract: extra columns must pass through unchanged and + must not affect feature retrieval correctness. + """ + now = sample_data + + # entity_df contains the join key, the PIT timestamp, AND a label column. + # Only ``driver_id`` is a join key; ``trip_success`` is the training label. + entity_df = pd.DataFrame( + { + "driver_id": [1, 1, 2], + "event_timestamp": [ + now - timedelta(hours=1, minutes=30), # driver 1 → conv_rate 0.5 + now - timedelta(minutes=30), # driver 1 → conv_rate 0.6 + now - timedelta(hours=1), # driver 2 → conv_rate 0.3 + ], + "trip_success": [1, 0, 1], # label column — must not enter entity key + } + ) + + job = MongoDBOfflineStoreOne.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df() + assert isinstance(result_df, pd.DataFrame) + assert len(result_df) == 3 + + # The label column must be preserved unchanged in the result. + assert "trip_success" in result_df.columns + assert sorted(result_df["trip_success"].tolist()) == [0, 1, 1] + + # Features must be non-null — if the label were folded into the entity key + # every lookup would miss and return None here. + assert result_df["conv_rate"].notna().all(), ( + "conv_rate is null for all rows — label column was likely included in " + "the entity key serialization, causing every MongoDB lookup to miss." + ) + + result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( + drop=True + ) + + # Driver 1, 1.5 hours before now → feature row from 2 hours ago + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) + # Driver 1, 30 minutes before now → feature row from 1 hour ago + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) + # Driver 2, 1 hour before now → feature row from 2 hours ago + assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) From f889e1038184187e70d170f13f5f69477603612b Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 09:12:19 -0400 Subject: [PATCH 33/76] Tests revealed possible name collision in pandas.merge_asof Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_one.py | 35 ++++++-- .../contrib/mongodb_offline_store/test_one.py | 88 +++++++++++++++++++ 2 files changed, 114 insertions(+), 9 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index 9aea8f2aeda..629abdace16 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -750,6 +750,15 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: columns={"event_timestamp": "_fv_ts"} ) + # So that merge_asof never does not cause column name collisions + # when FVs share the same feature names (full_feature_names=False), + # add fv name as prefix + fv_prefix = f"__fv_{fv_name}__" + temp_rename = { + f: f"{fv_prefix}{f}" for f in features if f in fv_df_subset.columns + } + fv_df_subset = fv_df_subset.rename(columns=temp_rename) + result = pd.merge_asof( result, fv_df_subset, @@ -759,20 +768,28 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: direction="backward", ) - # Apply TTL + # Apply TTL using temp column names if fv and fv.ttl: cutoff = result[event_timestamp_col] - fv.ttl stale_mask = result["_fv_ts"] < cutoff for feat in features: - if feat in result.columns: - result.loc[stale_mask, feat] = None - - # Rename features if full_feature_names + temp_col = f"{fv_prefix}{feat}" + if temp_col in result.columns: + result.loc[stale_mask, temp_col] = None + + # Rename from temp columns to final output names. + # If full_feature_names=False and two FVs share a feature name, + # the second FV's values overwrite the first — correct behaviour + # for un-prefixed retrieval (caller should use full_feature_names + # =True when FVs are intentionally named the same). for feat in features: - if feat in result.columns and full_feature_names: - result = result.rename(columns={feat: f"{fv_name}__{feat}"}) - elif feat not in result.columns: - col_name = f"{fv_name}__{feat}" if full_feature_names else feat + temp_col = f"{fv_prefix}{feat}" + col_name = f"{fv_name}__{feat}" if full_feature_names else feat + if temp_col in result.columns: + if col_name in result.columns: + result = result.drop(columns=[col_name]) + result = result.rename(columns={temp_col: col_name}) + elif col_name not in result.columns: result[col_name] = None result = result.drop(columns=["_fv_ts"], errors="ignore") diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py index f85531c5233..67ebd676337 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py @@ -489,6 +489,94 @@ def test_multiple_feature_views( assert result_df.loc[1, "mileage"] == 120000 +@_requires_docker +def test_multiple_feature_views_overlapping_feature_names( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Regression test: multi-FV join must not raise when FVs share feature names. + + When full_feature_names=False and multiple FVs define the same feature + (e.g. both have a ``score`` column), a naive merge_asof loop would produce + duplicate column names via pandas suffixes (_x, _y) on the second iteration + and then fail with "Passing 'suffixes' which cause duplicate columns" on the + third. The fix pre-renames each FV's columns to a unique per-FV temp prefix + before merging and then renames to the final output name. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + + now = datetime.now(tz=pytz.UTC) + + # Three FVs, all sharing feature name ``score``. + docs = [] + for fv_name, score_val in [ + ("fv_overlap_a", 1.0), + ("fv_overlap_b", 2.0), + ("fv_overlap_c", 3.0), + ]: + for driver_id in [1, 2]: + docs.append( + { + "entity_id": _make_entity_id({"driver_id": driver_id}), + "feature_view": fv_name, + "features": {"score": score_val + driver_id * 0.1}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + } + ) + collection.insert_many(docs) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + fvs = [] + feature_refs = [] + for fv_name in ["fv_overlap_a", "fv_overlap_b", "fv_overlap_c"]: + source = MongoDBSourceOne(name=fv_name) + fv = FeatureView( + name=fv_name, + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="score", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + fvs.append(fv) + feature_refs.append(f"{fv_name}:score") + + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + + # full_feature_names=True: each FV gets a distinct prefixed column. + job = MongoDBOfflineStoreOne.get_historical_features( + config=repo_config, + feature_views=fvs, + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=True, + ) + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + + assert len(result_df) == 2 + assert "fv_overlap_a__score" in result_df.columns + assert "fv_overlap_b__score" in result_df.columns + assert "fv_overlap_c__score" in result_df.columns + + # Driver 1: scores = base + 1*0.1 + assert result_df.loc[0, "fv_overlap_a__score"] == pytest.approx(1.1) + assert result_df.loc[0, "fv_overlap_b__score"] == pytest.approx(2.1) + assert result_df.loc[0, "fv_overlap_c__score"] == pytest.approx(3.1) + # Driver 2: scores = base + 2*0.1 + assert result_df.loc[1, "fv_overlap_a__score"] == pytest.approx(1.2) + assert result_df.loc[1, "fv_overlap_b__score"] == pytest.approx(2.2) + assert result_df.loc[1, "fv_overlap_c__score"] == pytest.approx(3.2) + + @_requires_docker def test_compound_join_keys( repo_config: RepoConfig, mongodb_connection_string: str From fcbd6096a3a5c90bae7f9785657727ccf8a13f5b Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 09:15:33 -0400 Subject: [PATCH 34/76] Add further (Large) benchmark tests Signed-off-by: Casey Clements --- .../mongodb_offline_store/benchmark.py | 1208 ++++++++++++++++- 1 file changed, 1186 insertions(+), 22 deletions(-) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py index fa7f99e06bf..fa09fe22106 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py @@ -18,11 +18,13 @@ Skip slow tests: pytest benchmark.py -v -s -m "not slow" """ +import resource +import sys import time import tracemalloc from dataclasses import dataclass, field from datetime import datetime, timedelta -from typing import Any, Dict, Generator, Optional +from typing import Any, Dict, Generator, List, Optional import pandas as pd import pytest @@ -125,6 +127,138 @@ def _make_entity_id(driver_id: int) -> bytes: return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) +# --------------------------------------------------------------------------- +# RSS measurement (per-operation delta) +# --------------------------------------------------------------------------- + + +def _rss_mb() -> float: + """Return current process RSS in MB. + + On macOS ``ru_maxrss`` is in bytes; on Linux it is in kilobytes. + Note: ``ru_maxrss`` is a *lifetime peak* reported by the kernel. We + snapshot it before and after each operation to compute the growth + attributable to that operation. Because the peak never decreases, the + delta accurately reflects the high-water-mark added by that operation + even after GC has freed Python objects. + """ + rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + if sys.platform == "darwin": + return rss / (1024 * 1024) + return rss / 1024 # Linux: KB → MB + + +# --------------------------------------------------------------------------- +# Batched data generators (memory-efficient at N > 100 K) +# --------------------------------------------------------------------------- + +_INSERT_BATCH = 50_000 # docs per insert_many call + + +def _generate_many_data_batched( + client: Any, + db_name: str, + collection_name: str, + num_entities: int, + num_features: int, + rows_per_entity: int = 5, +) -> datetime: + """Generate Many-schema data using batched inserts. + + Pre-computes one timestamp per historical row so we avoid calling + ``timedelta`` inside the inner loop. Feature values are simple + floats derived from (entity, row, feature) — no numpy required. + """ + import numpy as np + + collection = client[db_name][collection_name] + collection.drop() + + now = datetime.now(tz=pytz.UTC) + timestamps = [now - timedelta(hours=r) for r in range(rows_per_entity)] + feat_names = [f"feature_{f}" for f in range(num_features)] + + # Pre-generate all feature values as a 2-D numpy array for speed. + rng = np.random.default_rng(seed=42) + feature_matrix = rng.random((num_entities * rows_per_entity, num_features)).astype( + float + ) + + batch: List[Dict] = [] + idx = 0 + for entity_id in range(num_entities): + for row in range(rows_per_entity): + doc: Dict[str, Any] = { + "driver_id": entity_id, + "event_timestamp": timestamps[row], + } + row_vals = feature_matrix[idx] + for fi, fname in enumerate(feat_names): + doc[fname] = float(row_vals[fi]) + batch.append(doc) + idx += 1 + if len(batch) >= _INSERT_BATCH: + collection.insert_many(batch) + batch = [] + + if batch: + collection.insert_many(batch) + + return now + + +def _generate_one_data_batched( + client: Any, + db_name: str, + collection_name: str, + feature_view_name: str, + num_entities: int, + num_features: int, + rows_per_entity: int = 5, +) -> datetime: + """Generate One-schema data using batched inserts.""" + import numpy as np + + collection = client[db_name][collection_name] + # Do NOT drop — multiple feature views may share the collection. + + now = datetime.now(tz=pytz.UTC) + timestamps = [now - timedelta(hours=r) for r in range(rows_per_entity)] + feat_names = [f"feature_{f}" for f in range(num_features)] + + rng = np.random.default_rng(seed=42) + feature_matrix = rng.random((num_entities * rows_per_entity, num_features)).astype( + float + ) + + batch: List[Dict] = [] + idx = 0 + for entity_id in range(num_entities): + eid_bytes = _make_entity_id(entity_id) + for row in range(rows_per_entity): + row_vals = feature_matrix[idx] + features = { + feat_names[fi]: float(row_vals[fi]) for fi in range(num_features) + } + doc: Dict[str, Any] = { + "entity_id": eid_bytes, + "feature_view": feature_view_name, + "features": features, + "event_timestamp": timestamps[row], + "created_at": timestamps[row], + } + batch.append(doc) + idx += 1 + if len(batch) >= _INSERT_BATCH: + collection.insert_many(batch) + batch = [] + + if batch: + collection.insert_many(batch) + + return now + + @pytest.fixture(scope="module") def mongodb_container() -> Generator[MongoDbContainer, None, None]: """Start a MongoDB container for benchmarks.""" @@ -305,43 +439,49 @@ class FullBenchmarkResult: """Full benchmark results with all metrics.""" elapsed_seconds: float - peak_memory_mb: float - mongo_opcounters_delta: Dict[str, int] + peak_memory_mb: float # tracemalloc: Python-heap peak for this op + rss_delta_mb: float = 0.0 # RSS growth attributable to this op (OS-level) + mongo_opcounters_delta: Dict[str, int] = field(default_factory=dict) + status: str = "OK" # "OK" | "OOM" | "ERROR:" def _run_benchmark_full( func, mongo_client: Optional[Any] = None, ) -> FullBenchmarkResult: - """Run a benchmark capturing runtime, memory, and MongoDB metrics.""" - # Capture MongoDB metrics before + """Run a benchmark capturing runtime, memory (tracemalloc + RSS), and MongoDB metrics.""" mongo_before = None if mongo_client: mongo_before = MongoMetrics.capture(mongo_client) - # Start memory tracking + rss_before = _rss_mb() tracemalloc.start() - - # Run the benchmark start = time.perf_counter() - func() - elapsed = time.perf_counter() - start - # Capture peak memory + status = "OK" + try: + func() + except MemoryError: + status = "OOM" + except Exception as exc: + status = f"ERROR:{exc!s:.80}" + + elapsed = time.perf_counter() - start _, peak_memory = tracemalloc.get_traced_memory() tracemalloc.stop() - peak_memory_mb = peak_memory / (1024 * 1024) + rss_after = _rss_mb() - # Capture MongoDB metrics after - mongo_delta = {} + mongo_delta: Dict[str, int] = {} if mongo_client and mongo_before: mongo_after = MongoMetrics.capture(mongo_client) mongo_delta = mongo_before.delta(mongo_after) return FullBenchmarkResult( elapsed_seconds=elapsed, - peak_memory_mb=peak_memory_mb, + peak_memory_mb=peak_memory / (1024 * 1024), + rss_delta_mb=max(rss_after - rss_before, 0.0), mongo_opcounters_delta=mongo_delta, + status=status, ) @@ -353,14 +493,15 @@ def _print_benchmark_result( num_rows: Optional[int] = None, ) -> None: """Pretty print benchmark results.""" - print(f"\n[{impl}] {dimension_name}: {dimension_value:,}") - print(f" Time: {result.elapsed_seconds:.3f}s") - print(f" Memory: {result.peak_memory_mb:.1f} MB") - if num_rows: - rate = num_rows / result.elapsed_seconds if result.elapsed_seconds > 0 else 0 - print(f" Rate: {rate:,.0f} rows/s") + status_tag = f" [{result.status}]" if result.status != "OK" else "" + print(f"\n[{impl}] {dimension_name}: {dimension_value:,}{status_tag}") + print(f" Time: {result.elapsed_seconds:.3f}s") + print(f" Mem (trace): {result.peak_memory_mb:.1f} MB") + print(f" Mem (RSS Δ): {result.rss_delta_mb:.1f} MB") + if num_rows and result.elapsed_seconds > 0: + print(f" Rate: {num_rows / result.elapsed_seconds:,.0f} rows/s") if result.mongo_opcounters_delta: - print(f" Mongo ops: {result.mongo_opcounters_delta}") + print(f" Mongo ops: {result.mongo_opcounters_delta}") # ============================================================================= @@ -836,3 +977,1026 @@ def run_one(): finally: client.close() + + +# ============================================================================= +# Test 4: Large-Scale Memory Stress (N × M × P sweep) +# ============================================================================= +# +# Goal: demonstrate that Many's memory grows O(N × M × P) while One stays +# bounded by CHUNK_SIZE regardless of collection size. +# +# Dimensions held constant across this suite: +# M = STRESS_NUM_FEATURES = 50 (5× wider than the PR baseline) +# P = STRESS_ROWS_PER_ENTITY = 5 (slightly deeper history) +# +# Total collection docs = N × P. Memory for Many ≈ total_docs × M × 8 bytes +# × ~20 pandas/ibis overhead factor (empirically observed in PR benchmarks). +# One memory stays ≈ constant because it fetches CHUNK_SIZE entities at a time. + +STRESS_ENTITY_COUNTS = [50_000, 150_000, 300_000, 500_000] +STRESS_NUM_FEATURES = 50 +STRESS_ROWS_PER_ENTITY = 5 + + +def _create_many_fv_stress(num_features: int) -> tuple: + """Many source + FV for stress tests.""" + source = MongoDBSourceMany( + name="stress_benchmark", + database="benchmark_db", + collection="stress_benchmark", + timestamp_field="event_timestamp", + ) + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + fv = FeatureView( + name="stress_benchmark", + entities=[entity], + schema=schema, + source=source, + ttl=timedelta(days=1), + ) + return source, fv + + +def _create_one_fv_stress(num_features: int) -> tuple: + """One source + FV for stress tests.""" + source = MongoDBSourceOne( + name="stress_benchmark", timestamp_field="event_timestamp" + ) + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + fv = FeatureView( + name="stress_benchmark", + entities=[entity], + schema=schema, + source=source, + ttl=timedelta(days=1), + ) + return source, fv + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) # opt out of global 300s limit — large inserts take time +@pytest.mark.parametrize("num_entities", STRESS_ENTITY_COUNTS) +def test_scale_entities_stress_many( + mongodb_connection_string: str, many_config: RepoConfig, num_entities: int +) -> None: + """Many: memory stress across large entity counts with wide features. + + Collection has ``num_entities × STRESS_ROWS_PER_ENTITY`` total documents, + each with ``STRESS_NUM_FEATURES`` float fields. Many loads the entire + collection into memory, so peak memory grows linearly. A MemoryError is + caught and reported as "OOM" without failing the test. + """ + num_features = STRESS_NUM_FEATURES + rows_per_entity = STRESS_ROWS_PER_ENTITY + total_docs = num_entities * rows_per_entity + + client = MongoClient(mongodb_connection_string) + try: + print( + f"\n[MANY stress] Generating {total_docs:,} docs " + f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features)…" + ) + now = _generate_many_data_batched( + client, + "benchmark_db", + "stress_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=rows_per_entity, + ) + + _, fv = _create_many_fv_stress(num_features) + feature_refs = [f"stress_benchmark:feature_{i}" for i in range(num_features)] + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + safe, projected_mb, free_mb = _check_memory_for_many( + num_entities, rows_per_entity, num_features + ) + if not safe: + print( + f" [MANY stress] Skipping query — projected {projected_mb:,.0f} MB " + f"exceeds 80% of {free_mb:,.0f} MB free RAM. Would likely SIGKILL." + ) + result = FullBenchmarkResult( + elapsed_seconds=0, + peak_memory_mb=projected_mb, + status="SKIPPED:low_memory", + ) + else: + + def run_query(): + job = MongoDBOfflineStoreMany.get_historical_features( + config=many_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + + _print_benchmark_result( + "MANY", "Entities (stress)", num_entities, result, num_rows=num_entities + ) + print( + f" Collection docs: {total_docs:,} | " + f"Raw data ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" + ) + + finally: + client.close() + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) # opt out of global 300s limit — large inserts take time +@pytest.mark.parametrize("num_entities", STRESS_ENTITY_COUNTS) +def test_scale_entities_stress_one( + mongodb_connection_string: str, one_config: RepoConfig, num_entities: int +) -> None: + """One: memory stress across the same large entity counts. + + Because One fetches CHUNK_SIZE=50,000 entities at a time from MongoDB, + its peak memory is O(CHUNK_SIZE × M) regardless of N. All parametrize + values should complete without OOM. + """ + num_features = STRESS_NUM_FEATURES + rows_per_entity = STRESS_ROWS_PER_ENTITY + total_docs = num_entities * rows_per_entity + + client = MongoClient(mongodb_connection_string) + try: + client["benchmark_db"]["feature_history_stress"].drop() + print( + f"\n[ONE stress] Generating {total_docs:,} docs " + f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features)…" + ) + now = _generate_one_data_batched( + client, + "benchmark_db", + "feature_history_stress", + "stress_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=rows_per_entity, + ) + + # Use a separate RepoConfig pointing at the stress collection + stress_one_config = RepoConfig( + project="benchmark", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreOneConfig( + connection_string=mongodb_connection_string, + database="benchmark_db", + collection="feature_history_stress", + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + + _, fv = _create_one_fv_stress(num_features) + feature_refs = [f"stress_benchmark:feature_{i}" for i in range(num_features)] + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + def run_query(): + job = MongoDBOfflineStoreOne.get_historical_features( + config=stress_one_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + _print_benchmark_result( + "ONE", "Entities (stress)", num_entities, result, num_rows=num_entities + ) + print( + f" Collection docs: {total_docs:,} | " + f"Raw data ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" + ) + assert result.status == "OK", f"One should never OOM — got: {result.status}" + + finally: + client.close() + + +# ============================================================================= +# Test 5: OOM Crossover Demo +# ============================================================================= +# +# Runs BOTH implementations against the same large collection. +# - Many is expected to exhaust memory and raise MemoryError (caught → OOM). +# - One must complete successfully, demonstrating that chunked fetching +# keeps memory bounded regardless of total collection size. +# +# Scale selection: +# OOM_ENTITIES × OOM_ROWS × OOM_FEATURES total float values in MongoDB. +# Empirically, Many needs ~20 bytes per float (pandas + ibis + pyarrow copies). +# To exceed a 32 GB machine: 32 GB / 20 / 8 ≈ 200 M floats → ~400 K entities +# at (OOM_ROWS=5, OOM_FEATURES=100). +# +# If your machine has less RAM the crossover will happen at a smaller N; +# if it has more you may need to increase OOM_ENTITIES. + +OOM_ENTITIES = 400_000 # total unique entities +OOM_FEATURES = 100 # float features per document +OOM_ROWS = 5 # historical rows per entity +# → total docs = 2,000,000 | raw floats = 200,000,000 | raw bytes ≈ 1.6 GB +# → Many projected peak ≈ 32 GB (likely OOM on ≤32 GB machines) +# → One projected peak ≈ CHUNK_SIZE × OOM_FEATURES × 20 B ≈ 100 MB + + +def _available_memory_mb() -> float: + """Estimate *total* installed RAM in MB (used for reporting only).""" + try: + import subprocess + + out = subprocess.check_output(["sysctl", "-n", "hw.memsize"], text=True).strip() + return int(out) / (1024 * 1024) + except Exception: + pass + try: + with open("/proc/meminfo") as f: + for line in f: + if line.startswith("MemTotal:"): + return int(line.split()[1]) / 1024 + except Exception: + pass + return 16_384 # conservative fallback: 16 GB + + +def _free_memory_mb() -> float: + """Estimate *currently free/available* RAM in MB. + + On macOS uses ``vm_stat`` (free + purgeable pages at 16 KB each). + On Linux reads ``MemAvailable`` from ``/proc/meminfo``. + Falls back to a conservative 4 GB if neither is available. + + This is used to gate Many queries before they are attempted — avoiding + SIGKILL from the macOS jetsam memory-pressure system, which cannot be + caught by Python's ``except MemoryError``. + """ + try: + import subprocess + + out = subprocess.check_output(["vm_stat"], text=True) + page_size = 16_384 # 16 KB on Apple Silicon; 4 KB on Intel + try: + hw = subprocess.check_output( + ["sysctl", "-n", "hw.pagesize"], text=True + ).strip() + page_size = int(hw) + except Exception: + pass + free = purgeable = 0 + for line in out.splitlines(): + if line.startswith("Pages free:"): + free = int(line.split(":")[1].strip().rstrip(".")) + elif line.startswith("Pages purgeable:"): + purgeable = int(line.split(":")[1].strip().rstrip(".")) + return (free + purgeable) * page_size / (1024 * 1024) + except Exception: + pass + try: + with open("/proc/meminfo") as f: + for line in f: + if line.startswith("MemAvailable:"): + return int(line.split()[1]) / 1024 + except Exception: + pass + return 4_096 # conservative fallback: 4 GB + + +# Empirical overhead ratio: bytes consumed by Many per raw float value. +# Measured across stress test runs (tracemalloc peak / raw float count). +# 500K entities × P=5 × M=50 → 125M floats → 16.2 GB → ~130 bytes/float. +_MANY_BYTES_PER_FLOAT = 130 + + +def _many_projected_mb( + num_entities: int, rows_per_entity: int, num_features: int +) -> float: + """Project peak tracemalloc memory for a Many query in MB.""" + return num_entities * rows_per_entity * num_features * _MANY_BYTES_PER_FLOAT / 1e6 + + +def _check_memory_for_many( + num_entities: int, + rows_per_entity: int, + num_features: int, + safety_factor: float = 0.80, +) -> tuple: + """Return (safe_to_run, projected_mb, free_mb). + + ``safe_to_run`` is False when the projected peak exceeds + ``safety_factor × free_mb``, meaning we would likely be SIGKILL'd. + """ + projected = _many_projected_mb(num_entities, rows_per_entity, num_features) + free = _free_memory_mb() + return projected < free * safety_factor, projected, free + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) # opt out of global 300s limit — large inserts take time +def test_oom_crossover(mongodb_connection_string: str, many_config: RepoConfig) -> None: + """Demonstrate that Many OOMs where One succeeds (the key trade-off). + + Both implementations run against an identical dataset. Many loads the + entire collection into memory — at large enough scale it will raise + MemoryError, which is caught and reported. One uses chunked entity-filtered + queries and therefore completes with bounded memory. + + Expected output:: + + ╔══════════════════════════════════════════════════════════════════════╗ + ║ OOM CROSSOVER DEMO — Many vs One ║ + ╠══════════════════════════════════════════════════════════════════════╣ + ║ Scale: 400,000 entities × 5 rows × 100 features ║ + ║ 2,000,000 total docs | raw data ≈ 1,600 MB ║ + ╠═══════════════════════╦════════════════╦════════════════════════════╣ + ║ Metric ║ Many ║ One ║ + ╠═══════════════════════╬════════════════╬════════════════════════════╣ + ║ Status ║ OOM ❌ ║ OK ✅ ║ + ║ Time (s) ║ -- ║ 312.4 ║ + ║ Mem tracemalloc (MB) ║ -- ║ 312.0 ║ + ║ Mem RSS Δ (MB) ║ -- ║ 189.0 ║ + ╚═══════════════════════╩════════════════╩════════════════════════════╝ + """ + num_entities = OOM_ENTITIES + num_features = OOM_FEATURES + rows_per_entity = OOM_ROWS + total_docs = num_entities * rows_per_entity + raw_mb = total_docs * num_features * 8 / 1e6 + avail_mb = _available_memory_mb() + + print( + f"\n{'=' * 72}\n" + f"OOM CROSSOVER DEMO\n" + f" Scale : {num_entities:,} entities × {rows_per_entity} rows × {num_features} features\n" + f" Docs : {total_docs:,} total | raw ≈ {raw_mb:.0f} MB\n" + f" Machine: {avail_mb / 1024:.0f} GB RAM\n" + f"{'=' * 72}" + ) + + stress_one_cfg = RepoConfig( + project="benchmark", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreOneConfig( + connection_string=mongodb_connection_string, + database="benchmark_db", + collection="feature_history_oom", + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + + # Build Many source + FV pointing at the oom_benchmark collection + many_source = MongoDBSourceMany( + name="oom_benchmark", + database="benchmark_db", + collection="oom_benchmark", + timestamp_field="event_timestamp", + ) + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + many_schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + many_fv = FeatureView( + name="oom_benchmark", + entities=[entity], + schema=many_schema, + source=many_source, + ttl=timedelta(days=1), + ) + + # Build One source + FV (collection lives in feature_history_oom) + one_source = MongoDBSourceOne( + name="oom_benchmark", timestamp_field="event_timestamp" + ) + one_schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + one_fv = FeatureView( + name="oom_benchmark", + entities=[entity], + schema=one_schema, + source=one_source, + ttl=timedelta(days=1), + ) + + feature_refs = [f"oom_benchmark:feature_{i}" for i in range(num_features)] + + client = MongoClient(mongodb_connection_string) + try: + # --- Generate data for Many --- + print(f"Generating Many data ({total_docs:,} docs)…") + now = _generate_many_data_batched( + client, + "benchmark_db", + "oom_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=rows_per_entity, + ) + + # --- Generate data for One --- + print(f"Generating One data ({total_docs:,} docs)…") + client["benchmark_db"]["feature_history_oom"].drop() + _generate_one_data_batched( + client, + "benchmark_db", + "feature_history_oom", + "oom_benchmark", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=rows_per_entity, + ) + + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + # --- Run Many --- + print("Running Many (expect OOM on machines with ≤ 32 GB RAM)…") + + def run_many(): + many_job = MongoDBOfflineStoreMany.get_historical_features( + config=many_config, + feature_views=[many_fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return many_job.to_df() + + many_result = _run_benchmark_full(run_many, mongo_client=client) + + # --- Run One --- + print("Running One (should complete within bounded memory)…") + + def run_one(): + job = MongoDBOfflineStoreOne.get_historical_features( + config=stress_one_cfg, + feature_views=[one_fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return job.to_df() + + one_result = _run_benchmark_full(run_one, mongo_client=client) + + # --- Print crossover table --- + def _fmt(result: FullBenchmarkResult, field: str) -> str: + if result.status != "OK": + return f"{result.status} ❌" + if field == "status": + return "OK ✅" + if field == "time": + return f"{result.elapsed_seconds:.1f}s" + if field == "trace": + return f"{result.peak_memory_mb:.0f} MB" + if field == "rss": + return f"{result.rss_delta_mb:.0f} MB" + return "?" + + col = 28 + print("\n" + "╔" + "═" * col + "╦" + "═" * 16 + "╦" + "═" * 16 + "╗") + print(f"║ {'OOM CROSSOVER SUMMARY':<{col - 2}} ║ {'Many':^14} ║ {'One':^14} ║") + print("╠" + "═" * col + "╬" + "═" * 16 + "╬" + "═" * 16 + "╣") + print( + f"║ {'Status':<{col - 2}} ║ {_fmt(many_result, 'status'):^14} ║ {_fmt(one_result, 'status'):^14} ║" + ) + print( + f"║ {'Time':<{col - 2}} ║ {_fmt(many_result, 'time'):^14} ║ {_fmt(one_result, 'time'):^14} ║" + ) + print( + f"║ {'Mem tracemalloc (MB)':<{col - 2}} ║ {_fmt(many_result, 'trace'):^14} ║ {_fmt(one_result, 'trace'):^14} ║" + ) + print( + f"║ {'Mem RSS Δ (MB)':<{col - 2}} ║ {_fmt(many_result, 'rss'):^14} ║ {_fmt(one_result, 'rss'):^14} ║" + ) + print("╚" + "═" * col + "╩" + "═" * 16 + "╩" + "═" * 16 + "╝") + + # One must always complete regardless of machine size + assert one_result.status == "OK", ( + f"One must complete at any scale — got {one_result.status}" + ) + + finally: + client.close() + + +# ============================================================================= +# Test 6: Realistic Benchmark — P=30 (daily batch, 30-day TTL), Single FV +# ============================================================================= +# +# Motivation: the stress tests in Test 4 used P=5 and M=50, which inflated +# memory pressure via wide documents rather than deep history. A daily batch +# feature store with a 30-day TTL is far more common — every entity has 30 +# historical rows. With M=10 features (a typical narrow feature view) and +# P=30, the OOM crossover shifts to much lower entity counts: +# +# Projected Many memory = N × 30 × 10 floats × ~130 bytes/float (empirical) +# 100 K → ~3.9 GB (comfortable) +# 300 K → ~11.7 GB (heavy) +# 600 K → ~23.4 GB (near 32 GB limit) +# 900 K → ~35.1 GB → OOM expected on 32 GB machine +# +# One memory stays bounded at CHUNK_SIZE × 10 floats × overhead ≈ 650 MB +# regardless of N. + +REALISTIC_SINGLE_ENTITY_COUNTS = [100_000, 300_000, 600_000, 900_000] +REALISTIC_NUM_FEATURES = 10 +REALISTIC_ROWS_PER_ENTITY = 30 # daily data, 30-day TTL + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) +@pytest.mark.parametrize("num_entities", REALISTIC_SINGLE_ENTITY_COUNTS) +def test_realistic_p30_single_fv_many( + mongodb_connection_string: str, many_config: RepoConfig, num_entities: int +) -> None: + """Many: realistic daily-batch scenario (M=10, P=30), single feature view. + + Collection size = N × 30 docs. Many loads the entire collection, so memory + scales linearly with N. OOM is expected around 900 K entities on a 32 GB + machine; the error is caught and reported without failing the test. + """ + num_features = REALISTIC_NUM_FEATURES + rows_per_entity = REALISTIC_ROWS_PER_ENTITY + total_docs = num_entities * rows_per_entity + + client = MongoClient(mongodb_connection_string) + try: + print( + f"\n[MANY realistic-single] Generating {total_docs:,} docs " + f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features)…" + ) + now = _generate_many_data_batched( + client, + "benchmark_db", + "realistic_single", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=rows_per_entity, + ) + + source = MongoDBSourceMany( + name="realistic_single", + database="benchmark_db", + collection="realistic_single", + timestamp_field="event_timestamp", + ) + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + fv = FeatureView( + name="realistic_single", + entities=[entity], + schema=schema, + source=source, + ttl=timedelta(days=30), + ) + feature_refs = [f"realistic_single:feature_{i}" for i in range(num_features)] + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + safe, projected_mb, free_mb = _check_memory_for_many( + num_entities, rows_per_entity, num_features + ) + if not safe: + print( + f" [MANY realistic-single] Skipping query — projected {projected_mb:,.0f} MB " + f"exceeds 80% of {free_mb:,.0f} MB free RAM. Would likely SIGKILL." + ) + result = FullBenchmarkResult( + elapsed_seconds=0, + peak_memory_mb=projected_mb, + status="SKIPPED:low_memory", + ) + else: + + def run_query(): + many_job = MongoDBOfflineStoreMany.get_historical_features( + config=many_config, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return many_job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + + _print_benchmark_result( + "MANY", + "Entities (realistic P=30, 1 FV)", + num_entities, + result, + num_entities, + ) + print( + f" Collection docs: {total_docs:,} | Raw ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" + ) + + finally: + client.close() + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) +@pytest.mark.parametrize("num_entities", REALISTIC_SINGLE_ENTITY_COUNTS) +def test_realistic_p30_single_fv_one( + mongodb_connection_string: str, num_entities: int +) -> None: + """One: realistic daily-batch scenario (M=10, P=30), single feature view. + + One's memory ceiling is O(CHUNK_SIZE × M) and does not grow with N. + All parametrize values must complete without OOM. + """ + num_features = REALISTIC_NUM_FEATURES + rows_per_entity = REALISTIC_ROWS_PER_ENTITY + total_docs = num_entities * rows_per_entity + coll = "realistic_one_single" + + client = MongoClient(mongodb_connection_string) + try: + client["benchmark_db"][coll].drop() + print( + f"\n[ONE realistic-single] Generating {total_docs:,} docs " + f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features)…" + ) + now = _generate_one_data_batched( + client, + "benchmark_db", + coll, + "realistic_single", + num_entities=num_entities, + num_features=num_features, + rows_per_entity=rows_per_entity, + ) + + one_cfg = RepoConfig( + project="benchmark", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreOneConfig( + connection_string=mongodb_connection_string, + database="benchmark_db", + collection=coll, + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + source = MongoDBSourceOne( + name="realistic_single", timestamp_field="event_timestamp" + ) + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + fv = FeatureView( + name="realistic_single", + entities=[entity], + schema=schema, + source=source, + ttl=timedelta(days=30), + ) + feature_refs = [f"realistic_single:feature_{i}" for i in range(num_features)] + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + def run_query(): + one_job = MongoDBOfflineStoreOne.get_historical_features( + config=one_cfg, + feature_views=[fv], + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return one_job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + _print_benchmark_result( + "ONE", "Entities (realistic P=30, 1 FV)", num_entities, result, num_entities + ) + print( + f" Collection docs: {total_docs:,} | Raw ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" + ) + assert result.status == "OK", f"One must never OOM — got: {result.status}" + + finally: + client.close() + + +# ============================================================================= +# Test 7: Realistic Benchmark — P=30, 3 Feature Views joined simultaneously +# ============================================================================= +# +# In production, a training job typically joins 3–5 feature views. Many loads +# every FV's collection *independently*, so memory = sum of all collections. +# With 3 FVs (M=10, P=30): +# +# Projected Many memory = 3 × N × 30 × 10 floats × ~130 bytes/float +# 50 K → 3 × ~1.95 GB = ~5.85 GB +# 100 K → 3 × ~3.9 GB = ~11.7 GB +# 200 K → 3 × ~7.8 GB = ~23.4 GB (near 32 GB limit) +# 300 K → 3 × ~11.7 GB = ~35.1 GB → OOM expected +# +# One is unaffected: it chunks each FV query independently, so memory stays +# bounded at CHUNK_SIZE × M × overhead per FV, not the product. + +REALISTIC_MULTI_ENTITY_COUNTS = [50_000, 100_000, 200_000, 300_000] +REALISTIC_NUM_FEATURE_VIEWS = 3 + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) +@pytest.mark.parametrize("num_entities", REALISTIC_MULTI_ENTITY_COUNTS) +def test_realistic_p30_multi_fv_many( + mongodb_connection_string: str, many_config: RepoConfig, num_entities: int +) -> None: + """Many: realistic multi-FV join (3 FVs, M=10, P=30). + + Many must load all 3 collections before joining — memory is additive. + OOM is expected around 200K–300K entities on a 32 GB machine. + """ + num_features = REALISTIC_NUM_FEATURES + rows_per_entity = REALISTIC_ROWS_PER_ENTITY + num_fvs = REALISTIC_NUM_FEATURE_VIEWS + total_docs = num_fvs * num_entities * rows_per_entity + + fv_names = [f"realistic_fv_{i}" for i in range(num_fvs)] + coll_names = [f"realistic_many_fv_{i}" for i in range(num_fvs)] + + client = MongoClient(mongodb_connection_string) + try: + print( + f"\n[MANY realistic-multi] Generating {total_docs:,} docs across {num_fvs} FVs " + f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features each)…" + ) + now = None + for fv_name, coll_name in zip(fv_names, coll_names): + now = _generate_many_data_batched( + client, + "benchmark_db", + coll_name, + num_entities=num_entities, + num_features=num_features, + rows_per_entity=rows_per_entity, + ) + + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + feature_views = [] + for fv_name, coll_name in zip(fv_names, coll_names): + source = MongoDBSourceMany( + name=fv_name, + database="benchmark_db", + collection=coll_name, + timestamp_field="event_timestamp", + ) + schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + feature_views.append( + FeatureView( + name=fv_name, + entities=[entity], + schema=schema, + source=source, + ttl=timedelta(days=30), + ) + ) + + feature_refs = [ + f"{fv_name}:feature_{f}" + for fv_name in fv_names + for f in range(num_features) + ] + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + # Memory check: Many loads all num_fvs collections simultaneously. + safe, projected_mb, free_mb = _check_memory_for_many( + num_entities * num_fvs, rows_per_entity, num_features + ) + if not safe: + print( + f" [MANY realistic-multi] Skipping query — projected {projected_mb:,.0f} MB " + f"exceeds 80% of {free_mb:,.0f} MB free RAM. Would likely SIGKILL." + ) + result = FullBenchmarkResult( + elapsed_seconds=0, + peak_memory_mb=projected_mb, + status="SKIPPED:low_memory", + ) + else: + + def run_query(): + many_job = MongoDBOfflineStoreMany.get_historical_features( + config=many_config, + feature_views=feature_views, + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return many_job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + + _print_benchmark_result( + "MANY", + f"Entities (realistic P=30, {num_fvs} FVs)", + num_entities, + result, + num_entities, + ) + print( + f" Total docs loaded: {total_docs:,} | Raw ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" + ) + + finally: + client.close() + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) +@pytest.mark.parametrize("num_entities", REALISTIC_MULTI_ENTITY_COUNTS) +def test_realistic_p30_multi_fv_one( + mongodb_connection_string: str, num_entities: int +) -> None: + """One: realistic multi-FV join (3 FVs, M=10, P=30). + + All 3 FVs share a single MongoDB collection, discriminated by the + ``feature_view`` field. One chunks each FV query independently — its + memory ceiling is O(CHUNK_SIZE × M) regardless of N or the number of FVs. + All parametrize values must complete without OOM. + """ + num_features = REALISTIC_NUM_FEATURES + rows_per_entity = REALISTIC_ROWS_PER_ENTITY + num_fvs = REALISTIC_NUM_FEATURE_VIEWS + total_docs = num_fvs * num_entities * rows_per_entity + coll = "realistic_one_multi" + + fv_names = [f"realistic_fv_{i}" for i in range(num_fvs)] + + client = MongoClient(mongodb_connection_string) + try: + client["benchmark_db"][coll].drop() + print( + f"\n[ONE realistic-multi] Generating {total_docs:,} docs across {num_fvs} FVs " + f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features each)…" + ) + now = None + for fv_name in fv_names: + now = _generate_one_data_batched( + client, + "benchmark_db", + coll, + fv_name, + num_entities=num_entities, + num_features=num_features, + rows_per_entity=rows_per_entity, + ) + + one_cfg = RepoConfig( + project="benchmark", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreOneConfig( + connection_string=mongodb_connection_string, + database="benchmark_db", + collection=coll, + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + feature_views = [] + for fv_name in fv_names: + source = MongoDBSourceOne(name=fv_name, timestamp_field="event_timestamp") + schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + feature_views.append( + FeatureView( + name=fv_name, + entities=[entity], + schema=schema, + source=source, + ttl=timedelta(days=30), + ) + ) + + feature_refs = [ + f"{fv_name}:feature_{f}" + for fv_name in fv_names + for f in range(num_features) + ] + entity_df = pd.DataFrame( + { + "driver_id": list(range(num_entities)), + "event_timestamp": [now] * num_entities, + } + ) + + def run_query(): + one_job = MongoDBOfflineStoreOne.get_historical_features( + config=one_cfg, + feature_views=feature_views, + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=False, + ) + return one_job.to_df() + + result = _run_benchmark_full(run_query, mongo_client=client) + _print_benchmark_result( + "ONE", + f"Entities (realistic P=30, {num_fvs} FVs)", + num_entities, + result, + num_entities, + ) + print( + f" Total docs in collection: {total_docs:,} | Raw ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" + ) + assert result.status == "OK", f"One must never OOM — got: {result.status}" + + finally: + client.close() From 77863933154bc149b8c3e687281583f07f432b61 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 14:57:15 -0400 Subject: [PATCH 35/76] Upgrades from Devin comments. Class cache _index_initialized; get_expected_join_keys outside run_single Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_one.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index 629abdace16..d8bbe2a8195 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -386,6 +386,11 @@ def _serialize_entity_key_from_row( return serialize_entity_key(entity_key, entity_key_serialization_version) +# Module-level cache of (connection_string, db_name, collection_name) tuples for which +# indexes have already been created in this process. +_indexes_ensured: set = set() + + class MongoDBOfflineStoreOne(OfflineStore): """Native MongoDB offline store using single-collection schema. @@ -398,8 +403,6 @@ class MongoDBOfflineStoreOne(OfflineStore): - ``created_at``: ingestion time """ - _index_initialized: bool = False - @staticmethod def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: """Create recommended indexes on the feature_history collection. @@ -422,9 +425,9 @@ def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: background=True, ) - @classmethod - def _get_client_and_ensure_indexes(cls, config: RepoConfig) -> Any: - """Get a MongoClient and ensure indexes exist (once per process).""" + @staticmethod + def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: + """Get a MongoClient and ensure indexes exist once per (connection, db, collection).""" if MongoClient is None: raise FeastExtrasDependencyImportError( "mongodb", "pymongo is not installed." @@ -433,13 +436,18 @@ def _get_client_and_ensure_indexes(cls, config: RepoConfig) -> Any: config.offline_store.connection_string, driver=DRIVER_METADATA ) - if not cls._index_initialized: - cls._ensure_indexes( + cache_key = ( + config.offline_store.connection_string, + config.offline_store.database, + config.offline_store.collection, + ) + if cache_key not in _indexes_ensured: + MongoDBOfflineStoreOne._ensure_indexes( client, config.offline_store.database, config.offline_store.collection, ) - cls._index_initialized = True + _indexes_ensured.add(cache_key) return client @@ -635,6 +643,7 @@ def get_historical_features( fv_names = list(fv_to_features.keys()) fv_by_name = {fv.name: fv for fv in feature_views} + join_keys = get_expected_join_keys(project, feature_views, registry) # Chunk size for entity_df processing (bounds memory usage) CHUNK_SIZE = 50_000 @@ -670,11 +679,6 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: result[event_timestamp_col], utc=True ) - # Resolve join keys from the registry — the authoritative source. - # Do NOT derive from chunk columns: the entity_df may carry extra - # columns (labels, metadata) that must not enter the entity key. - join_keys = get_expected_join_keys(project, feature_views, registry) - # Serialize entity keys to bytes (same format as online store) result["_entity_id"] = result.apply( lambda row: _serialize_entity_key_from_row( From b8fa01c0e1e977879b0ed2c1099ee4624cca2266 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 15:08:03 -0400 Subject: [PATCH 36/76] Addressed PR comments Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_one.py | 4 +++- .../contrib/mongodb_offline_store/test_one.py | 4 +++- .../universal/data_sources/mongodb.py | 17 ++++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index d8bbe2a8195..4421743abf0 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -374,7 +374,9 @@ def _serialize_entity_key_from_row( entity_key.join_keys.append(key) value = row[key] val = ValueProto() - if isinstance(value, int): + if isinstance(value, bool): + val.bool_val = value + elif isinstance(value, int): val.int64_val = value elif isinstance(value, str): val.string_val = value diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py index 67ebd676337..3bcc8daa4d5 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py @@ -72,7 +72,9 @@ def _make_entity_id(join_keys: dict) -> bytes: entity_key.join_keys.append(key) val = ValueProto() value = join_keys[key] - if isinstance(value, int): + if isinstance(value, bool): + val.bool_val = value + elif isinstance(value, int): val.int64_val = value elif isinstance(value, str): val.string_val = value diff --git a/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py b/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py index 8eedc3b6957..0a0cfe648cc 100644 --- a/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py +++ b/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py @@ -176,20 +176,27 @@ def __init__(self, project_name: str, *args, **kwargs): self._entity_key_columns: Dict[str, list[str]] = {} def _serialize_entity_key(self, row: pd.Series, join_keys: list[str]) -> bytes: - """Serialize entity key columns to bytes.""" + """Serialize entity key columns to bytes. + + Join keys are sorted before serialization to match the order used by + _serialize_entity_key_from_row at query time. Without sorting, compound + join keys written in a different order would produce different bytes and + cause all features to be returned as NULL. + """ entity_key = EntityKeyProto() - for key in join_keys: + for key in sorted(join_keys): entity_key.join_keys.append(key) val = ValueProto() value = row[key] - if isinstance(value, int): + # bool must be checked before int: bool is a subclass of int in Python + if isinstance(value, bool): + val.bool_val = value + elif isinstance(value, int): val.int64_val = value elif isinstance(value, str): val.string_val = value elif isinstance(value, float): val.double_val = value - elif isinstance(value, bool): - val.bool_val = value else: val.string_val = str(value) entity_key.entity_values.append(val) From 6db7cce4d85bbae8f509981b1c3af636d75e585c Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 15:13:39 -0400 Subject: [PATCH 37/76] Apply lower bound via max(TTL) when all feature viewws in a chunk have one Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb_one.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index 4421743abf0..b012e83cfdf 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -646,6 +646,13 @@ def get_historical_features( fv_names = list(fv_to_features.keys()) fv_by_name = {fv.name: fv for fv in feature_views} join_keys = get_expected_join_keys(project, feature_views, registry) + # Lower-bound for event_timestamp queries. Only applicable when every FV + # has a TTL; if any FV is unbounded we cannot prune history. + max_ttl = ( + max(fv.ttl for fv in feature_views) + if all(fv.ttl for fv in feature_views) + else None + ) # Chunk size for entity_df processing (bounds memory usage) CHUNK_SIZE = 50_000 @@ -699,10 +706,13 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] + ts_filter: Dict[str, Any] = {"$lte": max_ts} + if max_ttl is not None: + ts_filter["$gte"] = max_ts - max_ttl query = { "entity_id": {"$in": batch_ids}, "feature_view": {"$in": fv_names}, - "event_timestamp": {"$lte": max_ts}, + "event_timestamp": ts_filter, } docs = list(coll.find(query, {"_id": 0})) all_feature_docs.extend(docs) From 596b12665311ef4353fe7f01da488c1b9b53f142 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 15:26:49 -0400 Subject: [PATCH 38/76] Add created_at to compound index so that materialization is correct if erroneuos data has been corrected. Remember, OfflineStores are append only. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb_one.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index b012e83cfdf..71d5ce90001 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -415,7 +415,17 @@ def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: collection = client[db_name][collection_name] # Check if an equivalent index already exists existing_indexes = collection.index_information() - target_key = [("entity_id", 1), ("feature_view", 1), ("event_timestamp", -1)] + # created_at is included so the full sort used by pull_latest_from_table_or_query + # {entity_id, event_timestamp DESC, created_at DESC} is satisfied entirely from + # the index (feature_view is bridged by the equality match). This matters for + # data corrections: a corrected document shares the same event_timestamp as the + # original but has a later created_at, and must win the $first selection. + target_key = [ + ("entity_id", 1), + ("feature_view", 1), + ("event_timestamp", -1), + ("created_at", -1), + ] for idx_info in existing_indexes.values(): if idx_info.get("key") == target_key: From c0173e0b047b51acfe9d7c998f3e74f2baad986c Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 15:47:00 -0400 Subject: [PATCH 39/76] Handdle numpy scalers in _serialize_entity_key_from_row as suggested. Signed-off-by: Casey Clements --- .../offline_stores/contrib/mongodb_offline_store/mongodb_one.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index 71d5ce90001..e504264410a 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -374,6 +374,8 @@ def _serialize_entity_key_from_row( entity_key.join_keys.append(key) value = row[key] val = ValueProto() + if isinstance(value, (int, float)) and hasattr(value, "item"): + value = value.item() # Convert numpy scalar to Python native if isinstance(value, bool): val.bool_val = value elif isinstance(value, int): From 1f80f47e38113faede05f8c40f412dffc393b68b Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 16:04:56 -0400 Subject: [PATCH 40/76] Add persist and tests Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_one.py | 91 ++++++++++-- .../mongodb_offline_store/test_many.py | 132 ++++++++++++++++++ .../contrib/mongodb_offline_store/test_one.py | 132 ++++++++++++++++++ 3 files changed, 347 insertions(+), 8 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index e504264410a..bb8ce4b263e 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -107,7 +107,11 @@ from pydantic import StrictStr from feast.data_source import DataSource -from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError +from feast.errors import ( + DataSourceNoNameException, + FeastExtrasDependencyImportError, + SavedDatasetLocationAlreadyExists, +) from feast.feature_view import FeatureView from feast.infra.key_encoding_utils import serialize_entity_key from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA @@ -122,9 +126,13 @@ ) from feast.infra.registry.base_registry import BaseRegistry from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto +from feast.protos.feast.core.SavedDataset_pb2 import ( + SavedDatasetStorage as SavedDatasetStorageProto, +) from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto from feast.repo_config import FeastConfigBaseModel, RepoConfig +from feast.saved_dataset import SavedDatasetStorage from feast.type_map import mongodb_to_feast_value_type from feast.value_type import ValueType @@ -286,6 +294,38 @@ def get_table_column_names_and_types( ] +class SavedDatasetMongoDBStorageOne(SavedDatasetStorage): + """Persists a Feast SavedDataset as a flat collection in MongoDB (one-store schema).""" + + _proto_attr_name = "custom_storage" + + def __init__(self, database: str, collection: str): + self._database = database + self._collection = collection + + @staticmethod + def from_proto( + storage_proto: SavedDatasetStorageProto, + ) -> "SavedDatasetMongoDBStorageOne": + config = json.loads(storage_proto.custom_storage.configuration) + return SavedDatasetMongoDBStorageOne( + database=config["database"], + collection=config["collection"], + ) + + def to_proto(self) -> SavedDatasetStorageProto: + return SavedDatasetStorageProto( + custom_storage=DataSourceProto.CustomSourceOptions( + configuration=json.dumps( + {"database": self._database, "collection": self._collection} + ).encode() + ) + ) + + def to_data_source(self) -> DataSource: + return MongoDBSourceOne(name=self._collection) + + def _infer_python_type_str(value: Any) -> Optional[str]: """Infer a Feast-compatible type string from a Python value.""" if value is None: @@ -329,11 +369,13 @@ def __init__( self, query_fn: Callable[[], pyarrow.Table], full_feature_names: bool, + config: RepoConfig, on_demand_feature_views: Optional[List[Any]] = None, metadata: Optional[RetrievalMetadata] = None, ): self._query_fn = query_fn self._full_feature_names = full_feature_names + self._config = config self._on_demand_feature_views = on_demand_feature_views or [] self._metadata = metadata @@ -357,12 +399,40 @@ def metadata(self) -> Optional[RetrievalMetadata]: def persist( self, - storage: Any, + storage: SavedDatasetStorage, allow_overwrite: bool = False, timeout: Optional[int] = None, ) -> None: - # TODO: Implement persist for native store - raise NotImplementedError("persist() not yet implemented for native store") + if not isinstance(storage, SavedDatasetMongoDBStorageOne): + raise ValueError( + f"MongoDBOneRetrievalJob.persist expected SavedDatasetMongoDBStorageOne, " + f"got {type(storage).__name__!r}." + ) + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + + db_name = storage._database or self._config.offline_store.database + coll_name = storage._collection + location = f"{db_name}.{coll_name}" + + client: Any = MongoClient( + self._config.offline_store.connection_string, + driver=DRIVER_METADATA, + tz_aware=True, + ) + try: + coll = client[db_name][coll_name] + if not allow_overwrite and coll.estimated_document_count() > 0: + raise SavedDatasetLocationAlreadyExists(location=location) + if allow_overwrite: + coll.drop() + records = self._to_arrow_internal().to_pylist() + if records: + coll.insert_many(records) + finally: + client.close() def _serialize_entity_key_from_row( @@ -539,7 +609,9 @@ def _run() -> pyarrow.Table: finally: client.close() - return MongoDBOneRetrievalJob(query_fn=_run, full_feature_names=False) + return MongoDBOneRetrievalJob( + query_fn=_run, full_feature_names=False, config=config + ) @staticmethod def pull_all_from_table_or_query( @@ -611,7 +683,9 @@ def _run() -> pyarrow.Table: finally: client.close() - return MongoDBOneRetrievalJob(query_fn=_run, full_feature_names=False) + return MongoDBOneRetrievalJob( + query_fn=_run, full_feature_names=False, config=config + ) @staticmethod def get_historical_features( @@ -661,7 +735,7 @@ def get_historical_features( # Lower-bound for event_timestamp queries. Only applicable when every FV # has a TTL; if any FV is unbounded we cannot prune history. max_ttl = ( - max(fv.ttl for fv in feature_views) + max(fv.ttl for fv in feature_views if fv.ttl is not None) if all(fv.ttl for fv in feature_views) else None ) @@ -703,7 +777,7 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: # Serialize entity keys to bytes (same format as online store) result["_entity_id"] = result.apply( lambda row: _serialize_entity_key_from_row( - row, join_keys, entity_key_version + row, list(join_keys), entity_key_version ), axis=1, ) @@ -866,4 +940,5 @@ def _run() -> pyarrow.Table: return MongoDBOneRetrievalJob( query_fn=_run, full_feature_names=full_feature_names, + config=config, ) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py index cbd43ea8d18..71e764dae43 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py @@ -19,10 +19,12 @@ from testcontainers.mongodb import MongoDbContainer from feast import Entity, FeatureView, Field +from feast.errors import SavedDatasetLocationAlreadyExists from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( MongoDBOfflineStoreMany, MongoDBOfflineStoreManyConfig, MongoDBSourceMany, + SavedDatasetMongoDBStorageMany, ) from feast.repo_config import RepoConfig from feast.types import Float64, Int64, String @@ -644,3 +646,133 @@ def test_compound_join_keys( assert result_df.loc[1, "app_opens"] == 55 # user 2, tablet assert result_df.loc[2, "app_opens"] == 25 + + +# ============================================================================= +# persist() tests +# ============================================================================= + + +@_requires_docker +def test_persist_writes_flat_result( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist() writes the flat joined DataFrame to the named destination collection.""" + now = sample_data + dest = "saved_ds_many_basic" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client.close() + + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [now, now - timedelta(hours=2)], + } + ) + + job = MongoDBOfflineStoreMany.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + job.persist(SavedDatasetMongoDBStorageMany(database="feast_test", collection=dest)) + + client = MongoClient(mongodb_connection_string) + try: + docs = list(client["feast_test"][dest].find({}, {"_id": 0})) + finally: + client.close() + + assert len(docs) == 2 + assert {"driver_id", "conv_rate", "acc_rate", "event_timestamp"}.issubset( + docs[0].keys() + ) + + +@_requires_docker +def test_persist_raises_if_collection_exists( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist() raises SavedDatasetLocationAlreadyExists when the destination is non-empty.""" + now = sample_data + dest = "saved_ds_many_no_overwrite" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client["feast_test"][dest].insert_one({"placeholder": True}) + client.close() + + entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) + + job = MongoDBOfflineStoreMany.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + with pytest.raises(SavedDatasetLocationAlreadyExists): + job.persist( + SavedDatasetMongoDBStorageMany(database="feast_test", collection=dest), + allow_overwrite=False, + ) + + +@_requires_docker +def test_persist_allow_overwrite( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist(allow_overwrite=True) replaces any existing collection contents.""" + now = sample_data + dest = "saved_ds_many_overwrite" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client["feast_test"][dest].insert_many([{"stale": True}, {"stale": True}]) + client.close() + + entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) + + job = MongoDBOfflineStoreMany.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + job.persist( + SavedDatasetMongoDBStorageMany(database="feast_test", collection=dest), + allow_overwrite=True, + ) + + client = MongoClient(mongodb_connection_string) + try: + docs = list(client["feast_test"][dest].find({}, {"_id": 0})) + finally: + client.close() + + assert len(docs) == 1 + assert "stale" not in docs[0] + assert "driver_id" in docs[0] diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py index 3bcc8daa4d5..e4d6d635f7c 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py @@ -31,11 +31,13 @@ from testcontainers.mongodb import MongoDbContainer from feast import Entity, FeatureView, Field +from feast.errors import SavedDatasetLocationAlreadyExists from feast.infra.key_encoding_utils import serialize_entity_key from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( MongoDBOfflineStoreOne, MongoDBOfflineStoreOneConfig, MongoDBSourceOne, + SavedDatasetMongoDBStorageOne, ) from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto @@ -766,3 +768,133 @@ def test_entity_df_with_extra_columns( assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) # Driver 2, 1 hour before now → feature row from 2 hours ago assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) + + +# ============================================================================= +# persist() tests +# ============================================================================= + + +@_requires_docker +def test_persist_writes_flat_result( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist() writes the flat joined DataFrame to the named destination collection.""" + now = sample_data + dest = "saved_ds_one_basic" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client.close() + + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [now, now - timedelta(hours=2)], + } + ) + + job = MongoDBOfflineStoreOne.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + job.persist(SavedDatasetMongoDBStorageOne(database="feast_test", collection=dest)) + + client = MongoClient(mongodb_connection_string) + try: + docs = list(client["feast_test"][dest].find({}, {"_id": 0})) + finally: + client.close() + + assert len(docs) == 2 + assert {"driver_id", "conv_rate", "acc_rate", "event_timestamp"}.issubset( + docs[0].keys() + ) + + +@_requires_docker +def test_persist_raises_if_collection_exists( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist() raises SavedDatasetLocationAlreadyExists when the destination is non-empty.""" + now = sample_data + dest = "saved_ds_one_no_overwrite" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client["feast_test"][dest].insert_one({"placeholder": True}) + client.close() + + entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) + + job = MongoDBOfflineStoreOne.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + with pytest.raises(SavedDatasetLocationAlreadyExists): + job.persist( + SavedDatasetMongoDBStorageOne(database="feast_test", collection=dest), + allow_overwrite=False, + ) + + +@_requires_docker +def test_persist_allow_overwrite( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist(allow_overwrite=True) replaces any existing collection contents.""" + now = sample_data + dest = "saved_ds_one_overwrite" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client["feast_test"][dest].insert_many([{"stale": True}, {"stale": True}]) + client.close() + + entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) + + job = MongoDBOfflineStoreOne.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + job.persist( + SavedDatasetMongoDBStorageOne(database="feast_test", collection=dest), + allow_overwrite=True, + ) + + client = MongoClient(mongodb_connection_string) + try: + docs = list(client["feast_test"][dest].find({}, {"_id": 0})) + finally: + client.close() + + assert len(docs) == 1 + assert "stale" not in docs[0] + assert "driver_id" in docs[0] From daaaf328fa989de3ae47a45912aaf924ed349e57 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 15 Apr 2026 16:22:50 -0400 Subject: [PATCH 41/76] Remove accidentally included design notes. Signed-off-by: Casey Clements --- design-notes/CASEY_SESSION_NOTES.md | 109 -------- design-notes/design-hybrid-with-batches.md | 239 ------------------ design-notes/native_implementation_notes.md | 191 -------------- design-notes/offline_store_design.md | 98 ------- ...ompt-mdb-fetch-pandas-join-with-batches.md | 108 -------- 5 files changed, 745 deletions(-) delete mode 100644 design-notes/CASEY_SESSION_NOTES.md delete mode 100644 design-notes/design-hybrid-with-batches.md delete mode 100644 design-notes/native_implementation_notes.md delete mode 100644 design-notes/offline_store_design.md delete mode 100644 design-notes/prompt-mdb-fetch-pandas-join-with-batches.md diff --git a/design-notes/CASEY_SESSION_NOTES.md b/design-notes/CASEY_SESSION_NOTES.md deleted file mode 100644 index 7a0b6f158f8..00000000000 --- a/design-notes/CASEY_SESSION_NOTES.md +++ /dev/null @@ -1,109 +0,0 @@ -# MongoDB Feast Integration — Session Notes -_Last updated: 2026-03-16. Resume here after OS upgrade._ - ---- - -## Status at a Glance - -| Component | Branch | Status | -|---|---|---| -| **Online Store** | `INTPYTHON-297-MongoDB-Feast-Integration` | ✅ **Merged to upstream/master** | -| **Offline Store** | `FEAST-OfflineStore-INTPYTHON-297` | 🔧 In progress — next focus | - ---- - -## Online Store — COMPLETE ✅ - -### What was done -- Implemented `MongoDBOnlineStore` with full sync + async API -- Refactored write path: extracted `_build_write_ops` static method to eliminate code - duplication between `online_write_batch` and `online_write_batch_async` -- Added Feast driver metadata to MongoDB client instantiations -- Registered MongoDB in the feast-operator (kubebuilder enums, `ValidOnlineStoreDBStorePersistenceTypes`, operator YAMLs) -- Updated online store status from `alpha` → `preview` in docs -- All 5 unit tests pass (including Docker-based testcontainers integration test) - -### Key files -- `sdk/python/feast/infra/online_stores/mongodb_online_store/mongodb.py` — main implementation -- `sdk/python/tests/unit/online_store/test_mongodb_online_retrieval.py` — test suite -- `sdk/python/tests/universal/feature_repos/universal/online_store/mongodb.py` — universal test repo config - -### Git history cleanup (this session) -The PR had two merge commits (`632e103a6`, `26ce79b37`) that blocked squash-and-merge. -Resolution: -1. `git fetch --all` -2. Created clean branch `FEAST-OnlineStore-INTPYTHON-297` from `upstream/master` -3. Cherry-picked all 47 commits (oldest → newest), skipping the two merge commits -4. Resolved conflicts: directory rename (`tests/integration/` → `tests/universal/`), - `pixi.lock` auto-resolved, `detect-secrets` false positives got `# pragma: allowlist secret` -5. Force-pushed to `INTPYTHON-297-MongoDB-Feast-Integration` — maintainer squash-merged ✅ - -### Versioning -Version is derived dynamically via `setuptools_scm` from git tags (no hardcoded version). -Latest tag at time of merge: **`v0.60.0`**. Feature ships in the next release after that. -Update JIRA with the next release tag once the maintainers cut it. - ---- - -## Offline Store — IN PROGRESS 🔧 - -### Branch -``` -FEAST-OfflineStore-INTPYTHON-297 -``` - -### Commits on branch (not yet in upstream/master) -``` -cd3eef677 Started work on full Mongo/MQL implementation. Kept MongoDBOfflineStoreIbis and MongoDBOfflineStoreNative -71469f69a feat: restore test-python-universal-mongodb-online Makefile target -904505244 fix: pass onerror to pkgutil.walk_packages -946d84e4c fix: broaden import exception handling in doctest runner -55de0e9b5 fix: catch FeastExtrasDependencyImportError in doctest runner -157a71d77 refactor: improve MongoDB offline store code quality -67632af2f feat: Add MongoDB offline store (ibis-based PIT join, v1 alpha) -``` - -### Key files -- `sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py` - - Contains **two prototype implementations**: - - `MongoDBOfflineStoreIbis` — uses Ibis for point-in-time joins (delegates to `get_historical_features_ibis`) - - `MongoDBOfflineStoreNative` — native MQL implementation (started in `cd3eef677`, in progress) -- `sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_source.py` — `MongoDBSource` data source - -### Architecture: Ibis vs Native -- **Ibis approach**: delegates PIT join to `feast.infra.offline_stores.ibis` helpers. - Pro: less code, consistency with other ibis-backed stores. - Con: requires ibis-mongodb connector; PIT correctness depends on ibis translation. -- **Native approach**: implements PIT join directly in MQL (MongoDB aggregation pipeline). - Pro: no extra dependency, full control. - Con: more complex; MQL aggregation pipelines can be verbose. -- Decision pending benchmarking / correctness validation between the two. - -### Next steps for offline store -1. Finish `MongoDBOfflineStoreNative` MQL implementation (started in latest commit) -2. Validate PIT correctness for both implementations against the Feast universal test suite -3. Run: `make test-python-universal-mongodb-offline` (target may need creating — see `71469f69a`) -4. Choose Ibis vs Native based on results; remove the other -5. Add to operator (same pattern as online store: kubebuilder enums, install.yaml) -6. Open PR — follow same DCO + linear history discipline as online store - ---- - -## Environment Notes - -- **Python env**: always use `uv run pytest ...` (uses `.venv` in repo root, Python 3.11) -- **Do NOT use**: system Python (`/Library/Frameworks/Python.framework/...`) or conda envs -- **Docker**: must be running for the testcontainers integration test -- **Stale container**: `72d14b345b6a` (mongo:latest, port 57120) — leftover from testing, safe to stop -- **DCO**: all commits must be signed: `git commit -s` -- **No push/merge without explicit user approval** - ---- - -## Git Workflow Reminder -To keep history clean (lesson from online store PR): -- Always branch from `upstream/master` (after `git fetch --all`) -- Never merge upstream into a feature branch — rebase or cherry-pick instead -- Before opening a PR, verify with: `git log --merges ^upstream/master --oneline` - (must return empty) - diff --git a/design-notes/design-hybrid-with-batches.md b/design-notes/design-hybrid-with-batches.md deleted file mode 100644 index 080986579ef..00000000000 --- a/design-notes/design-hybrid-with-batches.md +++ /dev/null @@ -1,239 +0,0 @@ -Native MongoDB Offline Store (Hybrid Design) - -Design Document - -Overview - -This document describes the design of the Native MongoDB Offline Store for Feast using a hybrid execution model. The system combines MongoDB’s strengths in indexed data retrieval with Python’s strengths in relational and temporal joins. - -The implementation uses a single-collection schema in MongoDB to store feature data across all FeatureViews and performs point-in-time (PIT) joins using a “fetch + pandas join” strategy. This replaces an earlier fully in-database $lookup approach that proved unscalable for large workloads. - -The result is a design that is performant, scalable, and aligned with Feast’s semantics. - -⸻ - -Data Model - -All FeatureViews share a single MongoDB collection (feature_history). Each document represents an observation of a FeatureView for a given entity at a specific timestamp. - -Each document contains: - • A serialized entity identifier (entity_id) - • A FeatureView identifier (feature_view) - • A subdocument of feature values (features) - • An event timestamp (event_timestamp) - • An ingestion timestamp (created_at) - -This schema supports: - • Sparse feature storage (not all features present in every document) - • Flexible schema evolution over time - • Efficient indexing across FeatureViews - -A compound index is maintained on: - • (entity_id, feature_view, event_timestamp DESC) - -This index supports efficient filtering by entity, FeatureView, and time range. - -⸻ - -Execution Model - -High-Level Strategy - -The system implements historical feature retrieval in three stages: - 1. Preprocessing (Python) - • Normalize timestamps to UTC - • Serialize entity keys into entity_id - • Partition the input entity_df into manageable chunks - 2. Data Fetching (MongoDB) - • Query MongoDB using $in on entity IDs - • Filter by FeatureView and time bounds - • Retrieve matching feature documents in batches - 3. Point-in-Time Join (Python) - • Convert MongoDB results into pandas DataFrames - • Perform per-FeatureView joins using merge_asof - • Apply TTL constraints and feature selection - -This design avoids per-row database joins and instead performs a small number of efficient indexed scans. - -⸻ - -Chunking and Batching - -To ensure scalability, the system separates concerns between: - • Chunk size (entity_df) -Controls memory usage in Python -Default: ~5,000 rows - • Batch size (MongoDB queries) -Controls query size and index efficiency -Default: ~1,000 entity IDs per query - -Each chunk of entity_df is processed independently: - • Entity IDs are extracted and deduplicated - • Feature data is fetched in batches - • Results are joined and accumulated - -This ensures: - • Bounded memory usage - • Predictable query performance - • Compatibility with large workloads - -⸻ - -Point-in-Time Join Semantics - -For each FeatureView: - • Feature data is sorted by (entity_id, event_timestamp) - • The entity dataframe is similarly sorted - • A backward merge_asof is performed - -This ensures: - • Only feature values with timestamps ≤ entity timestamp are used - • The most recent valid feature value is selected - -TTL constraints are applied after the join: - • If the matched feature timestamp is older than the allowed TTL window, the value is set to NULL - -⸻ - -Key Improvements in Current Design - -1. Projection (Reduced Data Transfer) - -The system now explicitly limits fields retrieved from MongoDB to only those required: - • entity_id - • feature_view - • event_timestamp - • Requested feature fields within features - -This reduces: - • Network overhead - • BSON decoding cost - • Memory usage in pandas - -This is especially important for wide FeatureViews or large documents. - -⸻ - -2. Bounded Time Filtering - -Queries now include both: - • An upper bound (<= max_ts) - • A lower bound (>= min_ts) - -This significantly reduces the amount of historical data scanned when: - • The entity dataframe spans a narrow time window - • The feature store contains deep history - -This optimization improves: - • Query latency - • Index selectivity - • Memory footprint of retrieved data - -Future enhancements may incorporate TTL-aware lower bounds. - -⸻ - -3. Correct Sorting for Temporal Joins - -The system ensures proper sorting before merge_asof: - • Both dataframes are sorted by (entity_id, timestamp) - -This is critical for correctness when: - • Multiple entities are processed in a single batch - • Data is interleaved across entities - -Without this, joins may silently produce incorrect results. - -⸻ - -Tradeoffs - -Advantages - • Scalability: Avoids O(n × m) behavior of correlated joins - • Flexibility: Supports sparse and evolving schemas - • Performance: Leverages MongoDB indexes efficiently - • Simplicity: Uses well-understood pandas join semantics - -Limitations - • Memory-bound joins: Requires chunking for large workloads - • Multiple passes: Each FeatureView requires a separate join - • No server-side joins: MongoDB is used only for filtering, not relational logic - -⸻ - -Comparison to Alternative Designs - -Full MongoDB Join ($lookup) - -Rejected due to: - • Poor scaling with large entity sets - • Repeated execution of correlated subqueries - • High latency (orders of magnitude slower) - -⸻ - -Ibis-Based Design - • Uses one collection per FeatureView - • Loads data into memory and performs joins in Python - -Comparison: - • Similar performance after hybrid redesign - • Simpler query model - • Less flexible schema - -The Native design trades simplicity for: - • Unified storage - • Better alignment with document-based ingestion - • More flexible feature evolution - -⸻ - -Operational Considerations - -Index Management - -Indexes are created lazily at runtime: - • Ensures correctness without manual setup - • Avoids placing responsibility on users - -Future improvements may include: - • Optional strict index validation - • Configuration-driven index management - -⸻ - -MongoDB Client Usage - -Each chunk currently uses a separate MongoDB client instance. - -This is acceptable for moderate workloads but may be optimized in the future by: - • Reusing a shared client per retrieval job - • Leveraging connection pooling more explicitly - -⸻ - -Future Work - -Several enhancements are possible: - 1. Streaming Joins - • Avoid materializing all feature data in memory - • Process data incrementally - 2. Adaptive Chunking - • Dynamically adjust chunk size based on memory pressure - 3. TTL Pushdown - • Incorporate TTL constraints into MongoDB queries - 4. Parallel Execution - • Process chunks concurrently for large workloads - -⸻ - -Conclusion - -The hybrid MongoDB + pandas design represents a significant improvement over the initial fully in-database approach. It aligns system responsibilities with the strengths of each component: - • MongoDB handles indexed filtering and retrieval - • Python handles temporal join logic - -With the addition of projection, bounded time filtering, and correct sorting, the system is now both performant and correct for large-scale historical feature retrieval. - -This design provides a strong foundation for further optimization and production use. - diff --git a/design-notes/native_implementation_notes.md b/design-notes/native_implementation_notes.md deleted file mode 100644 index 891751e56c2..00000000000 --- a/design-notes/native_implementation_notes.md +++ /dev/null @@ -1,191 +0,0 @@ -# Native MongoDB Offline Store Implementation Review - -## Overview - -This document reviews the native MongoDB offline store implementation (`mongodb_native.py`) in the context of Feast idioms, the MongoDB online store implementation, and best practices. - ---- - -## Schema Alignment: Online ↔ Offline - -### Online Store Schema (mongodb_online_store/mongodb.py) -```javascript -{ - "_id": bytes, // serialized entity key - "features": { - "": { - "": value - } - }, - "event_timestamps": { "": datetime }, - "created_timestamp": datetime -} -``` - -### Offline Store Schema (Native) -```javascript -{ - "_id": ObjectId(), - "entity_id": bytes, // serialized entity key (same format as online _id) - "feature_view": "driver_stats", // discriminator - "features": { "": value }, - "event_timestamp": datetime, - "created_at": datetime -} -``` - -### ✅ Alignment Strengths -1. **Entity key serialization**: Both use `serialize_entity_key()` from `key_encoding_utils.py` -2. **Nested features**: Both use `features: { ... }` subdocument pattern -3. **Timestamps**: Both track event and created timestamps - -### ⚠️ Alignment Concerns -1. **`_id` usage**: Online uses `_id` = entity_id; Offline uses `_id` = ObjectId() with separate `entity_id` field - - **Recommendation**: Consider using `_id` = `{entity_id, feature_view, event_timestamp}` compound key for offline, eliminating ObjectId overhead - -2. **Feature nesting depth**: Online nests by feature_view then feature; Offline nests only by feature (feature_view is top-level) - - This is intentional (offline is one doc per event; online is one doc per entity with all FVs) - ---- - -## Feast Idioms Compliance - -### ✅ Correctly Followed -1. **RetrievalJob pattern**: Returns `MongoDBNativeRetrievalJob` wrapping a `query_fn` closure -2. **Arrow output**: `_to_arrow_internal()` returns `pyarrow.Table` (hard requirement) -3. **Warnings for preview**: Uses `warnings.warn()` with `RuntimeWarning` -4. **Config inheritance**: `MongoDBOfflineStoreNativeConfig` extends `FeastConfigBaseModel` -5. **DataSource pattern**: `MongoDBSourceNative` extends `DataSource` with `from_proto`/`_to_proto_impl` - -### ⚠️ Missing or Incomplete -1. **`offline_write_batch`**: Not implemented (raises `NotImplementedError` in persist) - - Required for push sources and `feast materialize` reverse path - - Should accept `pyarrow.Table` and insert into `feature_history` collection - -2. **`write_logged_features`**: Not implemented - - Lower priority but needed for feature logging - -3. **`persist()` on RetrievalJob**: Not implemented - - Should write results to a new collection for saved datasets - ---- - -## MQL Pipeline Quality - -### ✅ Well Implemented -1. **`pull_all_from_table_or_query`**: Clean range scan with `$project` flattening features server-side -2. **`pull_latest_from_table_or_query`**: Proper `$sort` → `$group` → `$project` pattern -3. **`get_historical_features`**: Uses `$lookup` with correlated subpipeline for server-side PIT join -4. **Per-FV TTL via `$switch`**: Elegant solution for different TTLs per feature view - -### ⚠️ Potential Improvements -1. **Index usage in `$lookup`**: The `$expr` in `$match` may not use indexes efficiently - - MongoDB 5.0+ has better support for `$expr` index usage - - Consider adding `hint` option if performance is critical - -2. **Temp collection cleanup**: Currently uses `try/finally` but could benefit from context manager pattern - -3. **Connection pooling**: Each method creates a new `MongoClient`. The online store caches `_client` and `_collection` - - **Recommendation**: Add `_client` caching to the offline store class or use connection pooling - ---- - -## Comparison with Online Store Patterns - -| Aspect | Online Store | Offline Store (Native) | -|--------|--------------|------------------------| -| Client caching | `_client`, `_collection` instance vars | New client per operation | -| Async support | Yes (`AsyncMongoClient`) | No | -| Batch operations | `bulk_write` with `UpdateOne` | `insert_many` | -| Error handling | Raises `RuntimeError` for config mismatch | Raises `ValueError` | -| DriverInfo | ✅ Yes | ✅ Yes | - -### Recommendations -1. **Add client caching** to avoid connection overhead per query -2. **Consider async support** for large entity_df scenarios -3. **Standardize error types** (use `RuntimeError` or `FeastError` subclasses) - ---- - -## Missing Features for Production Readiness - -### High Priority -1. **`offline_write_batch`**: Insert Arrow table into feature_history - ```python - @staticmethod - def offline_write_batch( - config: RepoConfig, - feature_view: FeatureView, - table: pyarrow.Table, - progress: Optional[Callable[[int], Any]], - ): - # Convert Arrow → docs with schema: - # { entity_id, feature_view, features: {...}, event_timestamp, created_at } - # Then insert_many() - ``` - -2. **Index creation helper**: Document or auto-create the compound index - ```javascript - db.feature_history.createIndex({ - entity_id: 1, - feature_view: 1, - event_timestamp: -1 - }) - ``` - -3. **Connection pooling / client reuse** - -### Medium Priority -4. **`persist()` for saved datasets**: Write retrieval results to a collection -5. **`write_logged_features`**: For feature logging support -6. **Async operations**: Mirror online store's async pattern - -### Lower Priority -7. **Streaming cursor support**: For very large result sets -8. **Explain plan logging**: Debug mode to show MQL execution plan - ---- - -## Code Quality Observations - -### ✅ Good -- Clear docstrings explaining schema and index requirements -- Type hints throughout -- Helper functions extracted (`_ttl_to_ms`, `_build_ttl_gte_expr`, `_serialize_entity_key_from_row`) -- Proper cleanup of temp collections in `finally` block - -### ⚠️ Could Improve -- Some duplication in timestamp timezone handling (could extract helper) -- Magic strings like `"event_timestamp"`, `"created_at"` could be constants -- The `_run()` closures are large — consider extracting to separate methods - ---- - -## Test Coverage Assessment - -Current tests cover: -- ✅ `pull_latest_from_table_or_query` -- ✅ `pull_all_from_table_or_query` -- ✅ `get_historical_features` (PIT join) -- ✅ TTL filtering -- ✅ Multiple feature views -- ✅ Compound join keys - -Missing tests: -- ❌ `offline_write_batch` (not implemented) -- ❌ Empty result handling edge cases -- ❌ Very large entity_df (performance/memory) -- ❌ Concurrent access to temp collections -- ❌ Index usage verification (explain plans) - ---- - -## Summary - -The native implementation is a solid foundation with proper use of MQL aggregation pipelines. Key next steps: - -1. **Implement `offline_write_batch`** — Required for push sources -2. **Add client caching** — Match online store pattern -3. **Document/automate index creation** — Critical for performance -4. **Consider `_id` schema optimization** — Use compound `_id` instead of ObjectId + entity_id - diff --git a/design-notes/offline_store_design.md b/design-notes/offline_store_design.md deleted file mode 100644 index fbe7120a3c1..00000000000 --- a/design-notes/offline_store_design.md +++ /dev/null @@ -1,98 +0,0 @@ -# Corrected MongoDB OfflineStore Design - -## What the interface actually requires - -`RetrievalJob._to_arrow_internal` must return a `pyarrow.Table`. This is non-negotiable -because the compute engines call `retrieval_job.to_arrow()` directly: - -```python -# sdk/python/feast/infra/compute_engines/local/nodes.py -retrieval_job = create_offline_store_retrieval_job(...) -arrow_table = retrieval_job.to_arrow() # ← hard requirement -``` - -The compute engine then converts Arrow → proto tuples itself before calling -`OnlineStore.online_write_batch(data: List[Tuple[EntityKeyProto, ...]])`. -The offline store never sees the proto tuple format. - -`OfflineStore.offline_write_batch` (the push-source write path) takes a `pyarrow.Table` -— so Arrow is also the *input* format for writes. - -## The right approach — native aggregation, then Arrow - -The Couchbase offline store is the correct reference. It: -1. Expresses computation natively in the database (SQL++ window functions). -2. Iterates the cursor in Python. -3. Converts directly: `pa.Table.from_pylist(processed_rows)` — **no pandas intermediate**. - -MongoDB should follow the same pattern using its aggregation pipeline. - -## pull_latest_from_table_or_query - -The `$group` + `$sort` aggregation is the natural MongoDB equivalent of -`ROW_NUMBER() OVER(PARTITION BY entity ORDER BY timestamp DESC) = 1`: - -```python -pipeline = [ - {"$match": { - timestamp_field: {"$gte": start_date, "$lte": end_date} - }}, - {"$sort": {timestamp_field: -1, created_timestamp_column: -1}}, - {"$group": { - "_id": {k: f"${k}" for k in join_key_columns}, - **{f: {"$first": f"${f}"} for f in feature_name_columns}, - timestamp_field: {"$first": f"${timestamp_field}"}, - }}, -] -# cursor → pa.Table.from_pylist([doc for doc in collection.aggregate(pipeline)]) -``` - -No pandas. No Feast join utilities. The database does the work. - -## get_historical_features - -This is harder. The point-in-time join requires: for each (entity, entity_timestamp) row, -find the feature row with the latest `event_timestamp <= entity_timestamp`. - -MongoDB has no SQL window functions, but the aggregation pipeline can express this: - -``` -For each feature view: - $match: entity_ids in entity_df AND event_timestamp <= max(entity_timestamps) - $sort: entity_id, event_timestamp DESC - $lookup or unwind against entity_df rows - $match: event_timestamp <= entity_row.entity_timestamp (and TTL if set) - $group by (entity_id, entity_row_id): $first of features -``` - -This is complex but keeps computation in MongoDB and avoids loading the full history -into Python memory. The result cursor is then converted via `pa.Table.from_pylist()`. - -For an initial implementation it is acceptable to pull the filtered documents into -memory and do the join in Python (like the Dask store) — but this should be noted -as a known limitation, not the target design. - -## offline_write_batch - -Receives a `pyarrow.Table` from Feast (push-source path). Convert with -`table.to_pylist()` and `insert_many()` into the collection. - -## What changes from the previous design - -| Previous (incorrect) | Corrected | -|---------------------------------------------|---------------------------------------------| -| Pull docs into pandas, use offline_utils | Use MongoDB aggregation pipeline | -| pandas is the intermediate format | MongoDB cursor → `pa.Table.from_pylist()` | -| Arrow is an afterthought | Arrow is the required output of the job | -| Claimed online_write_batch takes Arrow | It takes proto tuples; compute engine converts | - -## Implementation order (unchanged) - -1. `MongoDBSource` — DataSource subclass (connection_string, database, collection, timestamp_field). -2. `MongoDBOfflineStoreConfig` — pydantic config. -3. `MongoDBRetrievalJob` — wraps aggregation pipeline, implements `_to_arrow_internal`. -4. `offline_write_batch` — `pyarrow.Table` → `insert_many`. -5. `pull_latest_from_table_or_query` — `$sort` + `$group` aggregation. -6. `pull_all_from_table_or_query` — `$match` time-range scan. -7. `get_historical_features` — aggregation pipeline PIT join (or in-memory fallback). - diff --git a/design-notes/prompt-mdb-fetch-pandas-join-with-batches.md b/design-notes/prompt-mdb-fetch-pandas-join-with-batches.md deleted file mode 100644 index 9bd8fb437c3..00000000000 --- a/design-notes/prompt-mdb-fetch-pandas-join-with-batches.md +++ /dev/null @@ -1,108 +0,0 @@ -Enhance MongoDBOfflineStoreNative.get_historical_features to support chunked execution for large entity_df, while preserving the existing fetch + pandas PIT join logic. - -Goals - • Prevent memory blowups for large entity_df - • Reuse the current implementation as much as possible - • Keep the code clean and idiomatic to Feast - -⸻ - -Requirements - -1. Add chunking based on entity_df size - • Introduce a constant: -``` python -CHUNK_SIZE = 5000 # make configurable configurable -``` - • If len(entity_df) <= CHUNK_SIZE: - • Run the existing _run() logic unchanged - • Else: - • Split entity_df into chunks of size CHUNK_SIZE - -⸻ - -2. Extract existing logic into reusable function -Refactor the current _run() implementation into a helper: -``` python -def _run_single(entity_subset_df: pd.DataFrame) -> pd.DataFrame: - ... -``` -This function should: - • Perform: - • entity_id serialization - • MongoDB fetch ($in query) - • pandas normalization - • per-feature-view merge_asof - • Return a pandas DataFrame (not Arrow) -3. Implement chunked execution -In _run(): -``` python -if len(entity_df) <= CHUNK_SIZE: - df = _run_single(entity_df) -else: - dfs = [] - for chunk in chunk_dataframe(entity_df, CHUNK_SIZE): - dfs.append(_run_single(chunk)) - df = pd.concat(dfs, ignore_index=True) -``` -4. Implement chunk helper -Add: -``` -def chunk_dataframe(df: pd.DataFrame, size: int): - for i in range(0, len(df), size): - yield df.iloc[i:i+size] -``` -5. Preserve ordering - • Ensure final DataFrame preserves original row order - • Use a _row_idx column if necessary -6. Handle edge cases -Ensure chunked version correctly handles: - • Empty MongoDB results - • Missing feature_views - • Missing features inside documents - • TTL filtering (already implemented in pandas) - -⸻ - -7. Return Arrow table -Final _run() must still return: -``` -pyarrow.Table.from_pandas(df, preserve_index=False) -``` -Constraints - • Do NOT reintroduce $lookup - • Do NOT use temp collections - • Do NOT duplicate large blocks of logic - • Keep code readable and maintainable - -⸻ - -Optional (nice-to-have) - • Add logging or debug print: - • number of chunks processed - • rows per chunk - -⸻ - -Outcome - • Small workloads behave exactly as before - • Large workloads are processed safely in chunks - • Performance remains close to Ibis for moderate sizes - • Memory usage is bounded - -⸻ - -🧠 Why this design is the right one - -This keeps your system: - -✅ Fast - • still uses vectorized joins - -✅ Scalable - • bounded memory - -✅ Clean - • no duplication - • no branching chaos - From 3a78f9d8af43200e87cc58dc407a3f05bab90c68 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 16 Apr 2026 10:03:47 -0400 Subject: [PATCH 42/76] Fix entity key serialization: per-FV join key types and numpy 2.0 compat _serialize_entity_key_from_row had two bugs: 1. numpy 2.0 broke the scalar unwrap: np.int64 no longer inherits from Python int, so `isinstance(value, (int, float)) and hasattr(value, "item")` never fired. All int entity keys were silently stored as their string representation, causing every MongoDB lookup to miss. Fixed by checking only `hasattr(value, "item")`. 2. INT32 vs INT64 mismatch: Python int always mapped to int64_val, but entities declared with value_type=INT32 are stored using int32_val (4 bytes vs 8 bytes). The serialized bytes never matched, returning NULL for all features. Fixed by passing declared ValueType per join key (derived from FeatureView.entity_columns, no registry required) and using the correct proto field in a new `if declared_type is not None` branch. Also adds test_int32_entity_key to pin the INT32 round-trip contract, and updates _make_entity_id to accept an optional value_types map so tests can write documents with the correct proto field for non-INT64 entities. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_one.py | 205 ++++++++++-------- .../contrib/mongodb_offline_store/test_one.py | 129 ++++++++++- 2 files changed, 234 insertions(+), 100 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index bb8ce4b263e..a6a88b8334d 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -436,26 +436,56 @@ def persist( def _serialize_entity_key_from_row( - row: pd.Series, join_keys: List[str], entity_key_serialization_version: int + row: pd.Series, + join_keys: List[str], + entity_key_serialization_version: int, + join_key_types: Optional[Dict[str, "ValueType"]] = None, ) -> bytes: - """Serialize entity key from a DataFrame row.""" + """Serialize entity key from a DataFrame row. + + Args: + row: DataFrame row containing join key values. + join_keys: Names of the join key columns. + entity_key_serialization_version: Version of entity key serialization. + join_key_types: Declared ValueType per join key, derived from the + FeatureView's entity_columns. When provided the correct proto field + is used (e.g. INT32 → int32_val). Without this, Python ``int`` + always maps to int64_val, which silently mismatches stored keys for + INT32 entities. + """ entity_key = EntityKeyProto() for key in sorted(join_keys): entity_key.join_keys.append(key) value = row[key] val = ValueProto() - if isinstance(value, (int, float)) and hasattr(value, "item"): - value = value.item() # Convert numpy scalar to Python native - if isinstance(value, bool): - val.bool_val = value - elif isinstance(value, int): - val.int64_val = value - elif isinstance(value, str): - val.string_val = value - elif isinstance(value, float): - val.double_val = value + if hasattr(value, "item"): + value = value.item() # Convert numpy scalar to Python native (numpy 2.0+) + declared_type = join_key_types.get(key) if join_key_types else None + if declared_type is not None: + if declared_type == ValueType.INT32: + val.int32_val = int(value) + elif declared_type == ValueType.INT64: + val.int64_val = int(value) + elif declared_type == ValueType.STRING: + val.string_val = str(value) + elif declared_type == ValueType.BYTES: + val.bytes_val = bytes(value) + elif declared_type == ValueType.UNIX_TIMESTAMP: + val.unix_timestamp_val = int(value) else: - val.string_val = str(value) + # No declared type: infer from Python runtime type. + # Python int is always mapped to int64_val, so INT32 entities + # require join_key_types to serialise correctly. + if isinstance(value, bool): + val.bool_val = value + elif isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + elif isinstance(value, float): + val.double_val = value + else: + val.string_val = str(value) entity_key.entity_values.append(val) return serialize_entity_key(entity_key, entity_key_serialization_version) @@ -729,16 +759,27 @@ def get_historical_features( fv_name, feat_name = ref.split(":", 1) fv_to_features[fv_name].append(feat_name) - fv_names = list(fv_to_features.keys()) fv_by_name = {fv.name: fv for fv in feature_views} - join_keys = get_expected_join_keys(project, feature_views, registry) - # Lower-bound for event_timestamp queries. Only applicable when every FV - # has a TTL; if any FV is unbounded we cannot prune history. - max_ttl = ( - max(fv.ttl for fv in feature_views if fv.ttl is not None) - if all(fv.ttl for fv in feature_views) - else None - ) + # Join keys resolved per FV. Using the union across all FVs would produce + # serialized bytes that never match stored documents when FVs have + # heterogeneous join keys (e.g. FV1 uses driver_id, FV2 uses customer_id). + fv_join_keys_by_name: Dict[str, List[str]] = { + fv.name: get_expected_join_keys(project, [fv], registry) + for fv in feature_views + } + # Declared ValueType per join key, derived directly from entity_columns on + # the FeatureView (no registry call needed). Used by + # _serialize_entity_key_from_row to pick the correct proto field so that + # INT32 entities produce int32_val bytes instead of int64_val bytes. + fv_join_key_types_by_name: Dict[str, Dict[str, ValueType]] = { + fv.name: { + fv.projection.join_key_map.get( + ec.name, ec.name + ): ec.dtype.to_value_type() + for ec in fv.entity_columns + } + for fv in feature_views + } # Chunk size for entity_df processing (bounds memory usage) CHUNK_SIZE = 50_000 @@ -774,75 +815,66 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: result[event_timestamp_col], utc=True ) - # Serialize entity keys to bytes (same format as online store) - result["_entity_id"] = result.apply( - lambda row: _serialize_entity_key_from_row( - row, list(join_keys), entity_key_version - ), - axis=1, - ) - - # Extract unique entity_ids and timestamp bounds for this chunk - unique_entity_ids = result["_entity_id"].unique().tolist() max_ts = result[event_timestamp_col].max() - # Fetch feature data in batches - all_feature_docs: List[Dict] = [] - - for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): - batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] - - ts_filter: Dict[str, Any] = {"$lte": max_ts} - if max_ttl is not None: - ts_filter["$gte"] = max_ts - max_ttl - query = { - "entity_id": {"$in": batch_ids}, - "feature_view": {"$in": fv_names}, - "event_timestamp": ts_filter, - } - docs = list(coll.find(query, {"_id": 0})) - all_feature_docs.extend(docs) - - # Handle empty result - if not all_feature_docs: - for fv_name, features in fv_to_features.items(): - for feat in features: - col_name = f"{fv_name}__{feat}" if full_feature_names else feat - result[col_name] = None - return result.drop(columns=["_entity_id"]) - - # Convert to DataFrame and flatten features subdoc - feature_df = pd.DataFrame(all_feature_docs) - feature_df = feature_df.rename(columns={"entity_id": "_entity_id"}) - - if "features" in feature_df.columns: - features_expanded = pd.json_normalize(feature_df["features"]) - feature_df = pd.concat( - [feature_df.drop(columns=["features"]), features_expanded], axis=1 - ) - - if feature_df["event_timestamp"].dt.tz is None: - feature_df["event_timestamp"] = pd.to_datetime( - feature_df["event_timestamp"], utc=True - ) - - # Sort result for merge_asof + # Sort once; merge_asof requires a sorted left table. result = result.sort_values(event_timestamp_col).reset_index(drop=True) - # Perform PIT join for each feature view + # Perform PIT join per feature view. Each FV serializes entity IDs + # using only its own join keys so the bytes match what was stored. for fv_name, features in fv_to_features.items(): fv = fv_by_name.get(fv_name) - fv_df = feature_df[feature_df["feature_view"] == fv_name].copy() + fv_join_keys = fv_join_keys_by_name[fv_name] + fv_join_key_types = fv_join_key_types_by_name[fv_name] + + result["_fv_entity_id"] = result.apply( + lambda row: _serialize_entity_key_from_row( + row, fv_join_keys, entity_key_version, fv_join_key_types + ), + axis=1, + ) - if fv_df.empty: + unique_entity_ids = result["_fv_entity_id"].unique().tolist() + + # Per-FV TTL as the query lower bound. + ts_filter: Dict[str, Any] = {"$lte": max_ts} + if fv and fv.ttl: + ts_filter["$gte"] = max_ts - fv.ttl + + fv_docs: List[Dict] = [] + for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): + batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] + query = { + "entity_id": {"$in": batch_ids}, + "feature_view": fv_name, + "event_timestamp": ts_filter, + } + fv_docs.extend(list(coll.find(query, {"_id": 0}))) + + if not fv_docs: for feat in features: col_name = f"{fv_name}__{feat}" if full_feature_names else feat result[col_name] = None + result = result.drop(columns=["_fv_entity_id"], errors="ignore") continue + fv_df = pd.DataFrame(fv_docs) + fv_df = fv_df.rename(columns={"entity_id": "_fv_entity_id"}) + + if "features" in fv_df.columns: + features_expanded = pd.json_normalize(fv_df["features"]) + fv_df = pd.concat( + [fv_df.drop(columns=["features"]), features_expanded], axis=1 + ) + + if fv_df["event_timestamp"].dt.tz is None: + fv_df["event_timestamp"] = pd.to_datetime( + fv_df["event_timestamp"], utc=True + ) + fv_df = fv_df.sort_values("event_timestamp").reset_index(drop=True) - merge_cols = ["_entity_id", "event_timestamp"] + [ + merge_cols = ["_fv_entity_id", "event_timestamp"] + [ f for f in features if f in fv_df.columns ] fv_df_subset = fv_df[ @@ -852,9 +884,7 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: columns={"event_timestamp": "_fv_ts"} ) - # So that merge_asof never does not cause column name collisions - # when FVs share the same feature names (full_feature_names=False), - # add fv name as prefix + # Prefix feature columns to avoid collisions when FVs share names. fv_prefix = f"__fv_{fv_name}__" temp_rename = { f: f"{fv_prefix}{f}" for f in features if f in fv_df_subset.columns @@ -866,11 +896,11 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: fv_df_subset, left_on=event_timestamp_col, right_on="_fv_ts", - by="_entity_id", + by="_fv_entity_id", direction="backward", ) - # Apply TTL using temp column names + # Apply TTL if fv and fv.ttl: cutoff = result[event_timestamp_col] - fv.ttl stale_mask = result["_fv_ts"] < cutoff @@ -879,11 +909,6 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: if temp_col in result.columns: result.loc[stale_mask, temp_col] = None - # Rename from temp columns to final output names. - # If full_feature_names=False and two FVs share a feature name, - # the second FV's values overwrite the first — correct behaviour - # for un-prefixed retrieval (caller should use full_feature_names - # =True when FVs are intentionally named the same). for feat in features: temp_col = f"{fv_prefix}{feat}" col_name = f"{fv_name}__{feat}" if full_feature_names else feat @@ -894,9 +919,11 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: elif col_name not in result.columns: result[col_name] = None - result = result.drop(columns=["_fv_ts"], errors="ignore") + result = result.drop( + columns=["_fv_entity_id", "_fv_ts"], errors="ignore" + ) - return result.drop(columns=["_entity_id"], errors="ignore") + return result def _run() -> pyarrow.Table: # Add row index to preserve original ordering diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py index e4d6d635f7c..c1521306658 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py @@ -18,7 +18,7 @@ """ from datetime import datetime, timedelta -from typing import Generator +from typing import Dict, Generator, Optional from unittest.mock import MagicMock import pandas as pd @@ -42,7 +42,7 @@ from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto from feast.repo_config import RepoConfig -from feast.types import Float64, Int64, String +from feast.types import Float64, Int32, Int64, String from feast.value_type import ValueType # Check if Docker is available @@ -67,21 +67,44 @@ ENTITY_KEY_VERSION = 3 -def _make_entity_id(join_keys: dict) -> bytes: - """Create serialized entity key from join key dict.""" +def _make_entity_id( + join_keys: dict, + value_types: Optional[Dict[str, ValueType]] = None, +) -> bytes: + """Create serialized entity key from join key dict. + + Args: + join_keys: Mapping of join key name → value. + value_types: Optional declared ValueType per key. When provided the + correct proto field is used (e.g. INT32 → int32_val), matching what + _serialize_entity_key_from_row produces when the FeatureView declares + that type. Omit to get the default int64_val/string_val inference. + """ entity_key = EntityKeyProto() for key in sorted(join_keys.keys()): entity_key.join_keys.append(key) val = ValueProto() value = join_keys[key] - if isinstance(value, bool): - val.bool_val = value - elif isinstance(value, int): - val.int64_val = value - elif isinstance(value, str): - val.string_val = value - else: + declared_type = value_types.get(key) if value_types else None + if declared_type == ValueType.INT32: + val.int32_val = int(value) + elif declared_type == ValueType.INT64: + val.int64_val = int(value) + elif declared_type == ValueType.STRING: val.string_val = str(value) + elif declared_type == ValueType.BYTES: + val.bytes_val = bytes(value) + elif declared_type == ValueType.UNIX_TIMESTAMP: + val.unix_timestamp_val = int(value) + else: + if isinstance(value, bool): + val.bool_val = value + elif isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + else: + val.string_val = str(value) entity_key.entity_values.append(val) return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) @@ -898,3 +921,87 @@ def test_persist_allow_overwrite( assert len(docs) == 1 assert "stale" not in docs[0] assert "driver_id" in docs[0] + + +@_requires_docker +def test_int32_entity_key( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """INT32 entity keys must round-trip through the correct proto field. + + Python ``int`` is indistinguishable from int32 vs int64 at the Python level. + Without the declared entity type, _serialize_entity_key_from_row always + writes int64_val (8 bytes), but a document written for an INT32 entity uses + int32_val (4 bytes) — different byte sequences, silent NULL return. + + This test pins that _serialize_entity_key_from_row uses the FeatureView's + declared ValueType to pick int32_val, so the bytes match. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + + now = datetime.now(tz=pytz.UTC) + + int32_types = {"order_id": ValueType.INT32} + docs = [ + { + "entity_id": _make_entity_id({"order_id": 1}, int32_types), + "feature_view": "order_features", + "features": {"amount": 100.0}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"order_id": 2}, int32_types), + "feature_view": "order_features", + "features": {"amount": 200.0}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + ] + collection.insert_many(docs) + client.close() + + source = MongoDBSourceOne(name="order_features") + order_entity = Entity( + name="order_id", join_keys=["order_id"], value_type=ValueType.INT32 + ) + fv = FeatureView( + name="order_features", + entities=[order_entity], + schema=[ + Field(name="order_id", dtype=Int32), + Field(name="amount", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + + entity_df = pd.DataFrame( + { + "order_id": [1, 2], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStoreOne.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["order_features:amount"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("order_id").reset_index(drop=True) + assert len(result_df) == 2 + # Without the fix, amount would be None for both rows: int64_val bytes (8 bytes) + # do not match the int32_val bytes (4 bytes) stored in the collection. + assert result_df["amount"].notna().all(), ( + "amount is null — INT32 entity key was serialized as INT64, producing " + "bytes that do not match the stored INT32 documents." + ) + assert result_df.loc[0, "amount"] == pytest.approx(100.0) + assert result_df.loc[1, "amount"] == pytest.approx(200.0) From 44abd927f742ff8ba75b9d9136664247f39cb28f Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 16 Apr 2026 11:34:28 -0400 Subject: [PATCH 43/76] Add offline_write_batch to MongoDBOfflineStoreOne MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the missing write path so that FeatureStore.write_to_offline_store() and the Arrow Flight offline server no longer raise NotImplementedError. Each row in the input pyarrow.Table is stored as one document in the single-collection schema (entity_id, feature_view, features, event_timestamp, created_at). Entity keys are serialized using the same type-aware _serialize_entity_key_from_row introduced in the previous commit, so bytes written here are guaranteed to match bytes produced by get_historical_features. Documents are appended (not upserted): multiple observations at the same (entity, feature_view, event_timestamp) are permitted and handled by the pull_latest_from_table_or_query $first/$created_at sort, which supports data corrections written with a later created_at. Adds test_offline_write_batch_round_trip which verifies both the document structure in the collection and the full write→read round-trip through get_historical_features. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_one.py | 130 ++++++++++++++++++ .../contrib/mongodb_offline_store/test_one.py | 85 ++++++++++++ 2 files changed, 215 insertions(+) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index a6a88b8334d..968e600fe5f 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -565,6 +565,136 @@ def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: return client + @staticmethod + def offline_write_batch( + config: RepoConfig, + feature_view: FeatureView, + table: pyarrow.Table, + progress: Optional[Callable[[int], Any]], + ) -> None: + """Write a batch of feature observations into the feature_history collection. + + This is the write counterpart to get_historical_features. Each row in + *table* is stored as one document using the single-collection schema:: + + { + "entity_id": , + "feature_view": , + "features": {: , ...}, + "event_timestamp": , + "created_at": , + } + + Called by FeatureStore.write_to_offline_store() and the Arrow Flight + offline server. Multiple writes for the same (entity, feature_view, + event_timestamp) are permitted — documents are appended, not replaced. + pull_latest_from_table_or_query resolves ties by picking the highest + created_at, so data corrections written with a later created_at will win. + + Args: + config: Feast repo configuration. + feature_view: The feature view being written; must have a + MongoDBSourceOne batch source. + table: Arrow table whose columns are the join key columns, feature + columns, event_timestamp, and optionally created_at. + progress: Optional callback invoked with the number of rows written + after each batch insert. + """ + if not isinstance(feature_view.batch_source, MongoDBSourceOne): + raise ValueError( + f"MongoDBOfflineStoreOne.offline_write_batch expected a MongoDBSourceOne " + f"batch source, got {type(feature_view.batch_source).__name__!r}." + ) + + entity_key_version = config.entity_key_serialization_version + db_name = config.offline_store.database + collection_name = config.offline_store.collection + + # Join key names and declared types — same derivation as get_historical_features. + join_key_types: Dict[str, ValueType] = { + feature_view.projection.join_key_map.get( + ec.name, ec.name + ): ec.dtype.to_value_type() + for ec in feature_view.entity_columns + } + join_keys = list(join_key_types.keys()) + + timestamp_field = feature_view.batch_source.timestamp_field + created_ts_col: Optional[str] = ( + feature_view.batch_source.created_timestamp_column or None + ) + + # Feature columns are everything that is not a join key or a timestamp. + reserved = set(join_keys) | {timestamp_field} + if created_ts_col: + reserved.add(created_ts_col) + feature_cols = [c for c in table.column_names if c not in reserved] + + df = table.to_pandas() + + # Ensure timestamp columns are tz-aware UTC. + for ts_col in [timestamp_field] + ([created_ts_col] if created_ts_col else []): + if ts_col in df.columns: + if not pd.api.types.is_datetime64_any_dtype(df[ts_col]): + df[ts_col] = pd.to_datetime(df[ts_col], utc=True) + elif df[ts_col].dt.tz is None: + df[ts_col] = df[ts_col].dt.tz_localize("UTC") + + # Serialize entity keys using the declared types (avoids INT32 vs INT64 mismatch). + df["_entity_id"] = df.apply( + lambda row: _serialize_entity_key_from_row( + row, join_keys, entity_key_version, join_key_types + ), + axis=1, + ) + + now = datetime.now(tz=timezone.utc) + + docs = [] + for _, row in df.iterrows(): + features: Dict[str, Any] = {} + for col in feature_cols: + val = row[col] + if pd.isna(val): + continue + if hasattr(val, "item"): + val = val.item() # numpy scalar → Python native for BSON + features[col] = val + + created_at = now + if created_ts_col and created_ts_col in df.columns: + ct = row[created_ts_col] + if not pd.isna(ct): + created_at = ( + ct.to_pydatetime() if hasattr(ct, "to_pydatetime") else ct + ) + + docs.append( + { + "entity_id": row["_entity_id"], + "feature_view": feature_view.name, + "features": features, + "event_timestamp": ( + row[timestamp_field].to_pydatetime() + if hasattr(row[timestamp_field], "to_pydatetime") + else row[timestamp_field] + ), + "created_at": created_at, + } + ) + + client = MongoDBOfflineStoreOne._get_client_and_ensure_indexes(config) + try: + coll = client[db_name][collection_name] + BATCH_SIZE = 10_000 + for i in range(0, len(docs), BATCH_SIZE): + batch = docs[i : i + BATCH_SIZE] + coll.insert_many(batch, ordered=False) + if progress: + progress(len(batch)) + finally: + client.close() + @staticmethod def pull_latest_from_table_or_query( config: RepoConfig, diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py index c1521306658..2dcdca06737 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py @@ -22,6 +22,7 @@ from unittest.mock import MagicMock import pandas as pd +import pyarrow import pytest import pytz @@ -1005,3 +1006,87 @@ def test_int32_entity_key( ) assert result_df.loc[0, "amount"] == pytest.approx(100.0) assert result_df.loc[1, "amount"] == pytest.approx(200.0) + + +@_requires_docker +def test_offline_write_batch_round_trip( + repo_config: RepoConfig, + mongodb_connection_string: str, + driver_fv: FeatureView, +) -> None: + """offline_write_batch writes documents that get_historical_features can read back. + + This is the end-to-end write→read contract: data written via the standard + Feast write path (offline_write_batch) must be retrievable via the standard + Feast read path (get_historical_features). It also verifies document structure + in the collection. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"]["feature_history"].drop() + client.close() + + now = datetime.now(tz=pytz.UTC) + + df = pd.DataFrame( + { + "driver_id": [1, 2], + "conv_rate": [0.8, 0.6], + "acc_rate": [0.9, 0.7], + "event_timestamp": [now - timedelta(hours=1), now - timedelta(hours=1)], + "created_at": [now, now], + } + ) + table = pyarrow.Table.from_pandas(df) + + MongoDBOfflineStoreOne.offline_write_batch( + config=repo_config, + feature_view=driver_fv, + table=table, + progress=None, + ) + + # Verify document structure in the collection. + client = MongoClient(mongodb_connection_string) + docs = list( + client["feast_test"]["feature_history"].find( + {"feature_view": "driver_stats"}, {"_id": 0} + ) + ) + client.close() + + assert len(docs) == 2 + doc = docs[0] + assert isinstance(doc["entity_id"], bytes), "entity_id must be serialized bytes" + assert doc["feature_view"] == "driver_stats" + assert set(doc["features"].keys()) == {"conv_rate", "acc_rate"} + assert "event_timestamp" in doc + assert "created_at" in doc + + # Verify round-trip: get_historical_features must retrieve the written data. + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStoreOne.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + assert len(result_df) == 2 + assert result_df["conv_rate"].notna().all(), ( + "conv_rate is null — offline_write_batch and get_historical_features " + "are producing different entity_id bytes." + ) + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.8) + assert result_df.loc[0, "acc_rate"] == pytest.approx(0.9) + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) + assert result_df.loc[1, "acc_rate"] == pytest.approx(0.7) From 63fab1b7507d8cba3b53c87ca3a4244bbdd821de Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 16 Apr 2026 18:31:45 -0400 Subject: [PATCH 44/76] mongodb_one: clarify pipeline sort rationale and avoid sparse-column expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace four TODO annotations in MongoDBOfflineStoreOne with accurate explanations and, where appropriate, a code fix: * pull_latest_from_table_or_query – \ stage: document why entity_id leads the sort key. The compound index (entity_id, feature_view, event_timestamp DESC, created_at DESC) can back the entire stage only when entity_id is first. Co-locating documents for the same entity also ensures \ picks the correct (most-recent event_timestamp, latest created_at) document. * get_historical_features – features expansion: replace pd.json_normalize + pd.concat with a targeted per-feature extraction. json_normalize expands every key ever present in any document; with schema evolution that produces many sparse columns and wastes memory. The new approach calls dict.get() for each requested feature only, keeping the DataFrame narrow regardless of how many unrelated feature keys exist in the store. * get_historical_features – fv_df sort: document that merge_asof requires the right-hand frame to be sorted by its join key before the column rename to _fv_ts. * get_historical_features – fv_df_subset copy: document that .copy() is required because column-selection on a DataFrame returns a view; calling rename() on a view raises SettingWithCopyWarning and can silently mutate the source frame. All 27 MongoDB unit tests continue to pass. Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_one.py | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index 968e600fe5f..6eafa8debd3 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -742,6 +742,12 @@ def pull_latest_from_table_or_query( "event_timestamp": {"$gte": start_utc, "$lte": end_utc}, } }, + # entity_id is first in the sort so the compound index + # (entity_id, feature_view, event_timestamp DESC, created_at DESC) + # can back this stage entirely. Documents of the same entity_id + # are then contiguous, so $first correctly picks the one with the + # highest event_timestamp (and highest created_at as a tiebreaker + # for data corrections written at the same event_timestamp). {"$sort": {"entity_id": 1, "event_timestamp": -1, "created_at": -1}}, { "$group": { @@ -992,21 +998,35 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: fv_df = fv_df.rename(columns={"entity_id": "_fv_entity_id"}) if "features" in fv_df.columns: - features_expanded = pd.json_normalize(fv_df["features"]) - fv_df = pd.concat( - [fv_df.drop(columns=["features"]), features_expanded], axis=1 - ) + # Extract only the feature columns that were requested for + # this FV. json_normalize would expand *every* key ever + # present in any document (schema evolution means different + # documents carry different keys), producing many sparse + # columns and unnecessary memory pressure. + for feat in features: + fv_df[feat] = fv_df["features"].apply( + lambda d, f=feat: d.get(f) if isinstance(d, dict) else None + ) + fv_df = fv_df.drop(columns=["features"]) if fv_df["event_timestamp"].dt.tz is None: fv_df["event_timestamp"] = pd.to_datetime( fv_df["event_timestamp"], utc=True ) + # merge_asof requires the right-hand DataFrame to be sorted by + # its join key. After the rename below, that key is _fv_ts; + # we sort on event_timestamp here (before the rename) so the + # physical order is correct when merge_asof consumes the frame. fv_df = fv_df.sort_values("event_timestamp").reset_index(drop=True) merge_cols = ["_fv_entity_id", "event_timestamp"] + [ f for f in features if f in fv_df.columns ] + # .copy() is required: column selection on a DataFrame returns + # a view; calling rename() on that view raises + # SettingWithCopyWarning and can produce unexpected results. + # The copy ensures we own the data before mutating it. fv_df_subset = fv_df[ [c for c in merge_cols if c in fv_df.columns] ].copy() From 90dd2245f59ee833646b3fa7b6b5051fea07a784 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 17 Apr 2026 17:52:43 -0400 Subject: [PATCH 45/76] Add mongodb_native.py: initial MQL-based offline store (pre-refactor snapshot) Ports the native MongoDB offline store from commit a1e3c9386 onto the feature branch as a baseline before the Atlas-first refactor. Uses a temp-collection + $lookup strategy for get_historical_features. Known issues (documented in design/): union join-key serialization, single entity_id across all FVs, _row_idx/iloc mismatch, missing created_at tiebreaker, and no offline_write_batch. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 794 ++++++++++++++++++ 1 file changed, 794 insertions(+) create mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py new file mode 100644 index 00000000000..c9cbae587a2 --- /dev/null +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -0,0 +1,794 @@ +# Copyright 2026 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Native MongoDB Offline Store Implementation. + +This module implements a MongoDB offline store using native MQL aggregation +pipelines. It uses a single-collection schema where all feature views share +one collection. It is event-based: each document represents an observation +of a FeatureView at a specific point in time. Each document may contain a +subset (0 or more) of the features defined in that FeatureView, all sharing +a single event_timestamp. + +Collection Index: + db.feature_history.create_index([ + ("feature_view", ASCENDING), + ("entity_id", ASCENDING), + ("event_timestamp", DESCENDING), + ]) + +Document Schema (example): + { + "_id": ObjectId(), + "entity_id": "", + "feature_view": "driver_stats", + "features": { + "rating": 4.91, + "trips_last_7d": 132 + }, + "event_timestamp": ISODate("2026-01-20T12:00:00Z"), + "created_at": ISODate("2026-01-20T12:00:05Z") + } + +Feature Freshness Semantics: + This implementation operates at *document-level freshness*, not + per-feature freshness. During retrieval (e.g. point-in-time joins), + the system selects the most recent document for a given + (entity_id, feature_view) that satisfies time constraints, and then + extracts all requested features from that document. + + As a result, if a newer document contains only a subset of features, + missing features will be returned as NULL—even if older documents + contained values for those features. The system does not backfill + individual feature values from earlier events. + + This behavior matches common Feast offline store semantics, but may + differ from systems that compute "latest value per feature". + +Schema Evolution ("Feature Creep"): + Because features are stored in a flexible subdocument, different + documents for the same FeatureView may contain different sets of + feature fields over time. This supports: + - adding new features without backfilling historical data + - partial writes or sparse feature computation + + However, it also implies: + - newly added features will be NULL for older events + - partially populated documents may lead to NULL values even + when older data contained those features + + Users should ensure that feature computation pipelines write + complete feature sets when consistent availability is required. + +Notes: + - Entity keys are serialized to ensure consistency with Feast’s + online store and to avoid type ambiguity. + - Point-in-time correctness is enforced per FeatureView. + - TTL (time-to-live) constraints are applied per FeatureView during + historical retrieval. +""" + +import json +import uuid +import warnings +from datetime import datetime, timezone +from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union + +import pandas as pd +import pyarrow + +try: + from pymongo import MongoClient +except ImportError: + MongoClient = None # type: ignore[assignment,misc] + +from pydantic import StrictStr + +from feast.data_source import DataSource +from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError +from feast.feature_view import FeatureView +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA +from feast.infra.offline_stores.offline_store import ( + OfflineStore, + RetrievalJob, + RetrievalMetadata, +) +from feast.infra.offline_stores.offline_utils import ( + infer_event_timestamp_from_entity_df, +) +from feast.infra.registry.base_registry import BaseRegistry +from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import FeastConfigBaseModel, RepoConfig +from feast.type_map import mongodb_to_feast_value_type +from feast.value_type import ValueType + + +class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): + """Configuration for the Native MongoDB offline store.""" + + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBOfflineStoreNative" + """Offline store type selector""" + + connection_string: StrictStr = "mongodb://localhost:27017" + """MongoDB connection URI""" + + database: StrictStr = "feast" + """MongoDB database name""" + + collection: StrictStr = "feature_history" + """Single collection name for all feature views""" + + +class MongoDBSourceNative(DataSource): + """A MongoDB data source for the native offline store. + + Unlike many data source implementations, this source does not map each + FeatureView to its own table or collection. Instead, all FeatureViews + share a single MongoDB collection (configured at the store level). + + Each document in that collection includes a ``feature_view`` field that + identifies which FeatureView it belongs to. The ``name`` of this data + source corresponds to that value and is used to filter documents during + queries. + """ + + def __init__( + self, + name: Optional[str] = None, + timestamp_field: str = "event_timestamp", + created_timestamp_column: str = "created_at", + field_mapping: Optional[Dict[str, str]] = None, + description: Optional[str] = "", + tags: Optional[Dict[str, str]] = None, + owner: Optional[str] = "", + ): + if name is None: + raise DataSourceNoNameException() + + super().__init__( + name=name, + timestamp_field=timestamp_field, + created_timestamp_column=created_timestamp_column, + field_mapping=field_mapping, + description=description, + tags=tags, + owner=owner, + ) + + def __hash__(self): + return super().__hash__() + + def __eq__(self, other): + if not isinstance(other, MongoDBSourceNative): + raise TypeError( + "Comparisons should only involve MongoDBSourceNative class objects." + ) + return ( + super().__eq__(other) + and self.timestamp_field == other.timestamp_field + and self.created_timestamp_column == other.created_timestamp_column + and self.field_mapping == other.field_mapping + ) + + @property + def feature_view_name(self) -> str: + """The feature_view discriminator value (same as source name).""" + return self.name + + def source_type(self) -> DataSourceProto.SourceType.ValueType: + return DataSourceProto.CUSTOM_SOURCE + + @staticmethod + def from_proto(data_source: DataSourceProto) -> "MongoDBSourceNative": + assert data_source.HasField("custom_options") + return MongoDBSourceNative( + name=data_source.name, + timestamp_field=data_source.timestamp_field, + created_timestamp_column=data_source.created_timestamp_column, + field_mapping=dict(data_source.field_mapping), + description=data_source.description, + tags=dict(data_source.tags), + owner=data_source.owner, + ) + + def _to_proto_impl(self) -> DataSourceProto: + return DataSourceProto( + name=self.name, + type=DataSourceProto.CUSTOM_SOURCE, + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBSourceNative", + field_mapping=self.field_mapping, + custom_options=DataSourceProto.CustomSourceOptions( + configuration=json.dumps({"feature_view": self.name}).encode() + ), + description=self.description, + tags=self.tags, + owner=self.owner, + timestamp_field=self.timestamp_field, + created_timestamp_column=self.created_timestamp_column, + ) + + def validate(self, config: RepoConfig): + pass + + @staticmethod + def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: + return mongodb_to_feast_value_type + + def get_table_query_string(self) -> str: + return f"feature_history[feature_view={self.name}]" + + def get_table_column_names_and_types( + self, config: RepoConfig + ) -> Iterable[Tuple[str, str]]: + """Sample documents to infer feature names and types. + + Queries documents matching this source's feature_view name and + inspects the ``features`` subdocument to determine schema. + """ + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + connection_string = config.offline_store.connection_string + db_name = config.offline_store.database + collection_name = config.offline_store.collection + client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) + try: + pipeline = [ + {"$match": {"feature_view": self.name}}, + {"$sample": {"size": 100}}, + ] + docs = list(client[db_name][collection_name].aggregate(pipeline)) + finally: + client.close() + + field_type_counts: Dict[str, Dict[str, int]] = {} + for doc in docs: + features = doc.get("features", {}) + for field, value in features.items(): + type_str = _infer_python_type_str(value) + if type_str is None: + continue + field_type_counts.setdefault(field, {}) + field_type_counts[field][type_str] = ( + field_type_counts[field].get(type_str, 0) + 1 + ) + + return [ + (field, max(counts, key=lambda t: counts[t])) + for field, counts in field_type_counts.items() + ] + + +def _infer_python_type_str(value: Any) -> Optional[str]: + """Infer a Feast-compatible type string from a Python value.""" + if value is None: + return None + if isinstance(value, bool): + return "bool" + if isinstance(value, int): + return "int" + if isinstance(value, float): + return "float" + if isinstance(value, str): + return "str" + if isinstance(value, bytes): + return "bytes" + if isinstance(value, datetime): + return "datetime" + if isinstance(value, list): + if not value: + return "list[str]" + elem_type = _infer_python_type_str(value[0]) + if elem_type: + return f"list[{elem_type}]" + return "list[str]" + return None + + +def _fetch_documents( + client: Any, + database: str, + collection: str, + pipeline: List[Dict], +) -> List[Dict]: + """Execute an aggregation pipeline and return documents.""" + return list(client[database][collection].aggregate(pipeline)) + + +class MongoDBNativeRetrievalJob(RetrievalJob): + """Retrieval job for native MongoDB offline store queries.""" + + def __init__( + self, + query_fn: Callable[[], pyarrow.Table], + full_feature_names: bool, + on_demand_feature_views: Optional[List[Any]] = None, + metadata: Optional[RetrievalMetadata] = None, + ): + self._query_fn = query_fn + self._full_feature_names = full_feature_names + self._on_demand_feature_views = on_demand_feature_views or [] + self._metadata = metadata + + @property + def full_feature_names(self) -> bool: + return self._full_feature_names + + @property + def on_demand_feature_views(self) -> List[Any]: + return self._on_demand_feature_views + + def _to_df_internal(self, timeout: Optional[int] = None) -> pd.DataFrame: + return self._to_arrow_internal(timeout).to_pandas() + + def _to_arrow_internal(self, timeout: Optional[int] = None) -> pyarrow.Table: + return self._query_fn() + + @property + def metadata(self) -> Optional[RetrievalMetadata]: + return self._metadata + + def persist( + self, + storage: Any, + allow_overwrite: bool = False, + timeout: Optional[int] = None, + ) -> None: + # TODO: Implement persist for native store + raise NotImplementedError("persist() not yet implemented for native store") + + +def _serialize_entity_key_from_row( + row: pd.Series, join_keys: List[str], entity_key_serialization_version: int +) -> bytes: + """Serialize entity key from a DataFrame row.""" + entity_key = EntityKeyProto() + for key in sorted(join_keys): + entity_key.join_keys.append(key) + value = row[key] + val = ValueProto() + if isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + elif isinstance(value, float): + val.double_val = value + else: + val.string_val = str(value) + entity_key.entity_values.append(val) + return serialize_entity_key(entity_key, entity_key_serialization_version) + + +def _ttl_to_ms(fv: FeatureView) -> Optional[int]: + """Convert FeatureView TTL to milliseconds.""" + if fv.ttl is None: + return None + return int(fv.ttl.total_seconds() * 1000) + + +def _build_ttl_gte_expr(feature_views: List[FeatureView]) -> Optional[Dict[str, Any]]: + """Build a $gte expression with per-FV TTL using $switch. + + Returns a MongoDB expression that evaluates to: + event_timestamp >= (entity_timestamp - ttl_for_this_feature_view) + + Each feature_view can have a different TTL, handled via $switch branches. + If no feature views have TTL, returns None (no filtering needed). + """ + branches = [] + + for fv in feature_views: + ttl_ms = _ttl_to_ms(fv) + if ttl_ms is None: + # No TTL for this FV - skip (effectively infinite history) + continue + + branches.append( + { + "case": {"$eq": ["$feature_view", fv.name]}, + "then": {"$subtract": ["$$ts", ttl_ms]}, + } + ) + + # If no TTLs at all, no lower bound needed + if not branches: + return None + + return { + "$gte": [ + "$event_timestamp", + { + "$switch": { + "branches": branches, + # Default: no lower bound (for FVs without TTL) + "default": {"$literal": 0}, + } + }, + ] + } + + +class MongoDBOfflineStoreNative(OfflineStore): + """Native MongoDB offline store using single-collection schema. + + All feature views share one collection (``feature_history``), with documents + containing: + - ``entity_id``: serialized entity key (bytes) + - ``feature_view``: field matching FeatureView name + - ``features``: subdocument with feature name/value pairs + - ``event_timestamp``: event time + - ``created_at``: ingestion time + """ + + _index_initialized: bool = False + + @staticmethod + def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: + """Create recommended indexes on the feature_history collection.""" + collection = client[db_name][collection_name] + collection.create_index( + [ + ("entity_id", 1), + ("feature_view", 1), + ("event_timestamp", -1), + ], + name="entity_fv_ts_idx", + ) + + @classmethod + def _get_client_and_ensure_indexes(cls, config: RepoConfig) -> Any: + """Get a MongoClient and ensure indexes exist (once per process).""" + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + client: Any = MongoClient( + config.offline_store.connection_string, driver=DRIVER_METADATA + ) + + if not cls._index_initialized: + cls._ensure_indexes( + client, + config.offline_store.database, + config.offline_store.collection, + ) + cls._index_initialized = True + + return client + + @staticmethod + def pull_latest_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str], + start_date: datetime, + end_date: datetime, + ) -> RetrievalJob: + if not isinstance(data_source, MongoDBSourceNative): + raise ValueError( + f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " + f"got {type(data_source).__name__!r}." + ) + warnings.warn( + "MongoDB offline store (native) is in preview. API may change without notice.", + RuntimeWarning, + ) + + db_name = config.offline_store.database + collection = config.offline_store.collection + feature_view_name = data_source.feature_view_name + + start_utc = start_date.astimezone(tz=timezone.utc) + end_utc = end_date.astimezone(tz=timezone.utc) + + # Build projection to flatten features subdoc to top-level fields + project_stage: Dict[str, Any] = { + "_id": 0, + "entity_id": "$doc.entity_id", + "event_timestamp": "$doc.event_timestamp", + } + if created_timestamp_column: + project_stage["created_at"] = "$doc.created_at" + for feat in feature_name_columns: + project_stage[feat] = f"$doc.features.{feat}" + + # Build aggregation pipeline + pipeline: List[Dict[str, Any]] = [ + { + "$match": { + "feature_view": feature_view_name, + "event_timestamp": {"$gte": start_utc, "$lte": end_utc}, + } + }, + {"$sort": {"entity_id": 1, "event_timestamp": -1, "created_at": -1}}, + { + "$group": { + "_id": "$entity_id", + "doc": {"$first": "$$ROOT"}, + } + }, + {"$project": project_stage}, + ] + + def _run() -> pyarrow.Table: + client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + try: + docs = _fetch_documents(client, db_name, collection, pipeline) + if not docs: + return pyarrow.Table.from_pydict({}) + + df = pd.DataFrame(docs) + if not df.empty and "event_timestamp" in df.columns: + if df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime( + df["event_timestamp"], utc=True + ) + return pyarrow.Table.from_pandas(df, preserve_index=False) + finally: + client.close() + + return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) + + @staticmethod + def pull_all_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str] = None, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None, + ) -> RetrievalJob: + if not isinstance(data_source, MongoDBSourceNative): + raise ValueError( + f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " + f"got {type(data_source).__name__!r}." + ) + warnings.warn( + "MongoDB offline store (native) is in preview. API may change without notice.", + RuntimeWarning, + ) + + db_name = config.offline_store.database + collection = config.offline_store.collection + feature_view_name = data_source.feature_view_name + + # Build match filter: feature_view + optional time range + match_filter: Dict[str, Any] = {"feature_view": feature_view_name} + if start_date or end_date: + ts_filter: Dict[str, Any] = {} + if start_date: + ts_filter["$gte"] = start_date.astimezone(tz=timezone.utc) + if end_date: + ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) + match_filter["event_timestamp"] = ts_filter + + # Build projection: flatten features subdoc to top-level fields + # This uses $getField to extract each feature from the features subdoc + project_stage: Dict[str, Any] = { + "_id": 0, + "entity_id": 1, + "event_timestamp": 1, + } + if created_timestamp_column: + project_stage["created_at"] = 1 + for feat in feature_name_columns: + project_stage[feat] = f"$features.{feat}" + + # Simple range scan pipeline - no sorting for efficiency + pipeline: List[Dict[str, Any]] = [ + {"$match": match_filter}, + {"$project": project_stage}, + ] + + def _run() -> pyarrow.Table: + client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + try: + docs = _fetch_documents(client, db_name, collection, pipeline) + if not docs: + return pyarrow.Table.from_pydict({}) + + df = pd.DataFrame(docs) + if not df.empty and "event_timestamp" in df.columns: + if df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime( + df["event_timestamp"], utc=True + ) + return pyarrow.Table.from_pandas(df, preserve_index=False) + finally: + client.close() + + return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) + + @staticmethod + def get_historical_features( + config: RepoConfig, + feature_views: List[FeatureView], + feature_refs: List[str], + entity_df: Union[pd.DataFrame, str], + registry: BaseRegistry, + project: str, + full_feature_names: bool = False, + ) -> RetrievalJob: + if isinstance(entity_df, str): + raise ValueError( + "MongoDBOfflineStoreNative does not support SQL entity_df strings. " + "Pass a pandas DataFrame instead." + ) + warnings.warn( + "MongoDB offline store (native) is in preview. API may change without notice.", + RuntimeWarning, + ) + + db_name = config.offline_store.database + feature_collection = config.offline_store.collection + entity_key_version = config.entity_key_serialization_version + + entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) + event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) + + # Map "feature_view:feature" refs → {fv_name: [feature, ...]} + fv_to_features: Dict[str, List[str]] = {} + for ref in feature_refs: + fv_name, feat_name = ref.split(":", 1) + fv_to_features.setdefault(fv_name, []).append(feat_name) + + fv_names = list(fv_to_features.keys()) + + # Build per-FV TTL expression using $switch + relevant_fvs = [fv for fv in feature_views if fv.name in fv_to_features] + ttl_expr = _build_ttl_gte_expr(relevant_fvs) + + def _run() -> pyarrow.Table: + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + + # Prepare entity_df: ensure timestamps are UTC and serialize entity keys + result = entity_df.copy() + if result[event_timestamp_col].dt.tz is None: + result[event_timestamp_col] = pd.to_datetime( + result[event_timestamp_col], utc=True + ) + + # Get join keys (all columns except event_timestamp) + entity_columns = [c for c in result.columns if c != event_timestamp_col] + + # Serialize entity keys to bytes (same format as online store) + result["_entity_id"] = result.apply( + lambda row: _serialize_entity_key_from_row( + row, entity_columns, entity_key_version + ), + axis=1, + ) + + # Build temp collection documents + temp_docs = [] + for _, row in result.iterrows(): + temp_docs.append( + { + "entity_id": row["_entity_id"], + "event_timestamp": row[event_timestamp_col], + "_row_idx": _, # Preserve original order + } + ) + + # Create temporary collection for query + temp_collection_name = f"tmp_entity_df_{uuid.uuid4().hex[:12]}" + + client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + try: + db = client[db_name] + temp_collection = db[temp_collection_name] + temp_collection.insert_many(temp_docs) + + # Build $lookup subpipeline with PIT join logic + # Match: entity_id, feature_view in list, event_timestamp <= entity.ts + match_conditions: List[Dict[str, Any]] = [ + {"$eq": ["$entity_id", "$$entity_id"]}, + {"$in": ["$feature_view", fv_names]}, + {"$lte": ["$event_timestamp", "$$ts"]}, + ] + # Add per-FV TTL filter using $switch + if ttl_expr is not None: + match_conditions.append(ttl_expr) + + lookup_pipeline: List[Dict[str, Any]] = [ + {"$match": {"$expr": {"$and": match_conditions}}}, + {"$sort": {"feature_view": 1, "event_timestamp": -1}}, + { + "$group": { + "_id": "$feature_view", + "doc": {"$first": "$$ROOT"}, + } + }, + ] + + # Main aggregation pipeline + pipeline: List[Dict[str, Any]] = [ + { + "$lookup": { + "from": feature_collection, + "let": { + "entity_id": "$entity_id", + "ts": "$event_timestamp", + }, + "pipeline": lookup_pipeline, + "as": "feature_rows", + } + }, + {"$sort": {"_row_idx": 1}}, # Preserve original order + ] + + docs = list(temp_collection.aggregate(pipeline)) + + finally: + # Cleanup temp collection + client[db_name][temp_collection_name].drop() + client.close() + + if not docs: + return pyarrow.Table.from_pydict({}) + + # Build result DataFrame + result = result.reset_index(drop=True) + rows = [] + for doc in docs: + # Start with entity columns from original entity_df + row_idx = doc["_row_idx"] + row = result.iloc[row_idx][ + entity_columns + [event_timestamp_col] + ].to_dict() + + # Extract features from each feature_view's matched doc + feature_rows_by_fv = { + fr["_id"]: fr["doc"] for fr in doc.get("feature_rows", []) + } + + # Extract features from each feature_view's matched doc + # TTL is already applied server-side via $switch expression + for fv_name, features in fv_to_features.items(): + fv_doc = feature_rows_by_fv.get(fv_name) + + for feat in features: + col_name = f"{fv_name}__{feat}" if full_feature_names else feat + if fv_doc is None: + row[col_name] = None + else: + row[col_name] = fv_doc.get("features", {}).get(feat) + + rows.append(row) + + result_df = pd.DataFrame(rows) + if not result_df.empty and event_timestamp_col in result_df.columns: + if result_df[event_timestamp_col].dt.tz is None: + result_df[event_timestamp_col] = pd.to_datetime( + result_df[event_timestamp_col], utc=True + ) + return pyarrow.Table.from_pandas(result_df, preserve_index=False) + + return MongoDBNativeRetrievalJob( + query_fn=_run, + full_feature_names=full_feature_names, + ) From 073368884dd36f66ac2a1b42c13d5a154207a555 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 17 Apr 2026 17:59:31 -0400 Subject: [PATCH 46/76] Refactor mongodb_native: Atlas-first $documents+$lookup PIT join MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the temp-collection + single $lookup approach with a pure-MQL $documents + chained $lookup pipeline that runs entirely on Atlas. Correctness fixes ported from mongodb_one.py: - _serialize_entity_key_from_row: add join_key_types param for INT32 vs INT64 correctness; unwrap numpy scalars for numpy 2.0 compatibility - Per-FV join key derivation from fv.entity_columns — union keys produce bytes that never match stored documents when FVs have different entities - reset_index(drop=True) before building $documents; _row_idx is then a safe iloc index regardless of the caller's DataFrame index - created_at tiebreaker in $lookup sort for data corrections Architecture changes: - $documents injects entity_df chunk as a virtual collection; no temp collection is created, no orphan risk on crash - One $lookup per FV with let:{eid: $_entity_ids., ts: $_ts}; each FV matches its own entity key bytes — solves the single-entity_id bug - TTL expressed inline per FV inside its $lookup subpipeline (no $switch) - $project {features:1} inside subpipeline + final $project drop _entity_ids to minimise data transferred from Atlas - allowDiskUse=True on all aggregate() calls - 4-key compound index adds created_at for correction tiebreaking - Module-level _indexes_ensured set (vs class-level bool) for correctness across multiple RepoConfigs in the same process Parity with mongodb_one.py: - offline_write_batch implemented - SavedDatasetMongoDBStorageNative + persist() implemented - RetrievalJob carries config Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_native.py | 970 ++++++++++++------ 1 file changed, 643 insertions(+), 327 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py index c9cbae587a2..10ce735fceb 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py @@ -13,78 +13,94 @@ # limitations under the License. """ -Native MongoDB Offline Store Implementation. +Native MongoDB Offline Store — Atlas-first, pure-MQL implementation. -This module implements a MongoDB offline store using native MQL aggregation -pipelines. It uses a single-collection schema where all feature views share -one collection. It is event-based: each document represents an observation -of a FeatureView at a specific point in time. Each document may contain a -subset (0 or more) of the features defined in that FeatureView, all sharing -a single event_timestamp. +All feature views share a single ``feature_history`` collection, discriminated +by a ``feature_view`` field. Historical retrieval (point-in-time join) is +executed entirely as a MongoDB aggregation pipeline using ``$documents`` + +``$lookup``, so all sorting, grouping, and TTL filtering run on the Atlas +cluster rather than on the client. + +Minimum server version: MongoDB 5.1 (Atlas M10+ or self-managed 5.1+). +``$documents`` (the stage that injects the entity DataFrame into the pipeline +without a temp collection) was introduced in 5.1. + +Collection Index (compound, covers both PIT join and pull-latest): -Collection Index: db.feature_history.create_index([ - ("feature_view", ASCENDING), - ("entity_id", ASCENDING), - ("event_timestamp", DESCENDING), + ("entity_id", ASCENDING), + ("feature_view", ASCENDING), + ("event_timestamp", DESCENDING), + ("created_at", DESCENDING), ]) -Document Schema (example): + The ``created_at`` key is required so that data corrections (two documents + with the same entity_id + feature_view + event_timestamp) are resolved + deterministically: the document with the later created_at wins. + +Document Schema: + { - "_id": ObjectId(), - "entity_id": "", - "feature_view": "driver_stats", + "_id": ObjectId(), + "entity_id": Binary(...), # serialized entity key + "feature_view": "driver_stats", "features": { - "rating": 4.91, - "trips_last_7d": 132 + "conv_rate": 0.72, + "trips_last_7d": 132 }, "event_timestamp": ISODate("2026-01-20T12:00:00Z"), - "created_at": ISODate("2026-01-20T12:00:05Z") + "created_at": ISODate("2026-01-20T12:00:05Z") } +PIT Join Strategy (get_historical_features): + + For each chunk of the entity DataFrame: + + 1. Python serializes one entity key per (row, feature_view), using only + that feature view's declared join keys and value types. This is the + key correctness invariant: driver_stats is keyed by {driver_id} and + customer_stats by {customer_id}; using the union would produce bytes + that never match stored documents. + + 2. A single ``$documents`` stage injects the chunk as a virtual collection: + each document carries ``_row_idx``, ``_ts``, and a per-FV + ``_entity_ids`` subdocument. + + 3. One ``$lookup`` stage per feature view performs the correlated PIT join + entirely on Atlas: + - match: entity_id == $$eid, feature_view == , + event_timestamp <= $$ts, (optional) event_timestamp >= $$ts - ttl + - sort: event_timestamp DESC, created_at DESC + - limit: 1 + + 4. A final ``$project`` drops the entity-id payload before results are + sent over the network. + + 5. Python assembles the flat result DataFrame from the matched docs. + Feature Freshness Semantics: - This implementation operates at *document-level freshness*, not - per-feature freshness. During retrieval (e.g. point-in-time joins), - the system selects the most recent document for a given - (entity_id, feature_view) that satisfies time constraints, and then - extracts all requested features from that document. - - As a result, if a newer document contains only a subset of features, - missing features will be returned as NULL—even if older documents - contained values for those features. The system does not backfill - individual feature values from earlier events. - - This behavior matches common Feast offline store semantics, but may - differ from systems that compute "latest value per feature". - -Schema Evolution ("Feature Creep"): - Because features are stored in a flexible subdocument, different - documents for the same FeatureView may contain different sets of - feature fields over time. This supports: - - adding new features without backfilling historical data - - partial writes or sparse feature computation - - However, it also implies: - - newly added features will be NULL for older events - - partially populated documents may lead to NULL values even - when older data contained those features - - Users should ensure that feature computation pipelines write - complete feature sets when consistent availability is required. - -Notes: - - Entity keys are serialized to ensure consistency with Feast’s - online store and to avoid type ambiguity. - - Point-in-time correctness is enforced per FeatureView. - - TTL (time-to-live) constraints are applied per FeatureView during - historical retrieval. + + This implementation uses document-level freshness: the most recent document + for (entity_id, feature_view) whose event_timestamp <= entity row's + timestamp is selected, and all requested features are read from that one + document. If a newer document is missing a feature that an older document + had, the value is NULL. This matches standard Feast offline store semantics. """ import json -import uuid import warnings +from collections import defaultdict from datetime import datetime, timezone -from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union +from typing import ( + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Tuple, + Union, +) import pandas as pd import pyarrow @@ -97,7 +113,11 @@ from pydantic import StrictStr from feast.data_source import DataSource -from feast.errors import DataSourceNoNameException, FeastExtrasDependencyImportError +from feast.errors import ( + DataSourceNoNameException, + FeastExtrasDependencyImportError, + SavedDatasetLocationAlreadyExists, +) from feast.feature_view import FeatureView from feast.infra.key_encoding_utils import serialize_entity_key from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA @@ -107,22 +127,42 @@ RetrievalMetadata, ) from feast.infra.offline_stores.offline_utils import ( + get_expected_join_keys, infer_event_timestamp_from_entity_df, ) from feast.infra.registry.base_registry import BaseRegistry from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto +from feast.protos.feast.core.SavedDataset_pb2 import ( + SavedDatasetStorage as SavedDatasetStorageProto, +) from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto from feast.repo_config import FeastConfigBaseModel, RepoConfig +from feast.saved_dataset import SavedDatasetStorage from feast.type_map import mongodb_to_feast_value_type from feast.value_type import ValueType +# --------------------------------------------------------------------------- +# Index tracking — keyed by (connection_string, database, collection) so that +# multiple RepoConfigs pointing at different clusters never share state. +# --------------------------------------------------------------------------- +_indexes_ensured: set = set() + +# Chunk size for entity_df processing. Each chunk becomes one $documents +# stage; keep it small enough that the aggregation command stays well under +# MongoDB's 16 MB BSON document limit. +_CHUNK_SIZE = 50_000 + + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- + class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): - """Configuration for the Native MongoDB offline store.""" + """Configuration for the native MongoDB offline store (Atlas-first).""" type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBOfflineStoreNative" - """Offline store type selector""" connection_string: StrictStr = "mongodb://localhost:27017" """MongoDB connection URI""" @@ -131,20 +171,19 @@ class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): """MongoDB database name""" collection: StrictStr = "feature_history" - """Single collection name for all feature views""" + """Single collection name shared by all feature views""" -class MongoDBSourceNative(DataSource): - """A MongoDB data source for the native offline store. +# --------------------------------------------------------------------------- +# DataSource +# --------------------------------------------------------------------------- + - Unlike many data source implementations, this source does not map each - FeatureView to its own table or collection. Instead, all FeatureViews - share a single MongoDB collection (configured at the store level). +class MongoDBSourceNative(DataSource): + """MongoDB data source for the native (Atlas-first) offline store. - Each document in that collection includes a ``feature_view`` field that - identifies which FeatureView it belongs to. The ``name`` of this data - source corresponds to that value and is used to filter documents during - queries. + All feature views share a single collection. The ``name`` of this source + doubles as the ``feature_view`` discriminator value stored in each document. """ def __init__( @@ -159,7 +198,6 @@ def __init__( ): if name is None: raise DataSourceNoNameException() - super().__init__( name=name, timestamp_field=timestamp_field, @@ -187,7 +225,7 @@ def __eq__(self, other): @property def feature_view_name(self) -> str: - """The feature_view discriminator value (same as source name).""" + """The feature_view discriminator value stored in each document.""" return self.name def source_type(self) -> DataSourceProto.SourceType.ValueType: @@ -210,7 +248,10 @@ def _to_proto_impl(self) -> DataSourceProto: return DataSourceProto( name=self.name, type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBSourceNative", + data_source_class_type=( + "feast.infra.offline_stores.contrib.mongodb_offline_store" + ".mongodb_native.MongoDBSourceNative" + ), field_mapping=self.field_mapping, custom_options=DataSourceProto.CustomSourceOptions( configuration=json.dumps({"feature_view": self.name}).encode() @@ -235,32 +276,30 @@ def get_table_query_string(self) -> str: def get_table_column_names_and_types( self, config: RepoConfig ) -> Iterable[Tuple[str, str]]: - """Sample documents to infer feature names and types. - - Queries documents matching this source's feature_view name and - inspects the ``features`` subdocument to determine schema. - """ + """Sample documents to infer feature names and types.""" if MongoClient is None: raise FeastExtrasDependencyImportError( "mongodb", "pymongo is not installed." ) - connection_string = config.offline_store.connection_string - db_name = config.offline_store.database - collection_name = config.offline_store.collection - client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) + client: Any = MongoClient( + config.offline_store.connection_string, driver=DRIVER_METADATA + ) try: pipeline = [ {"$match": {"feature_view": self.name}}, {"$sample": {"size": 100}}, ] - docs = list(client[db_name][collection_name].aggregate(pipeline)) + docs = list( + client[config.offline_store.database][ + config.offline_store.collection + ].aggregate(pipeline) + ) finally: client.close() field_type_counts: Dict[str, Dict[str, int]] = {} for doc in docs: - features = doc.get("features", {}) - for field, value in features.items(): + for field, value in doc.get("features", {}).items(): type_str = _infer_python_type_str(value) if type_str is None: continue @@ -275,54 +314,62 @@ def get_table_column_names_and_types( ] -def _infer_python_type_str(value: Any) -> Optional[str]: - """Infer a Feast-compatible type string from a Python value.""" - if value is None: - return None - if isinstance(value, bool): - return "bool" - if isinstance(value, int): - return "int" - if isinstance(value, float): - return "float" - if isinstance(value, str): - return "str" - if isinstance(value, bytes): - return "bytes" - if isinstance(value, datetime): - return "datetime" - if isinstance(value, list): - if not value: - return "list[str]" - elem_type = _infer_python_type_str(value[0]) - if elem_type: - return f"list[{elem_type}]" - return "list[str]" - return None +# --------------------------------------------------------------------------- +# SavedDataset storage +# --------------------------------------------------------------------------- + + +class SavedDatasetMongoDBStorageNative(SavedDatasetStorage): + """Persists a SavedDataset as a flat MongoDB collection (native store).""" + + _proto_attr_name = "custom_storage" + + def __init__(self, database: str, collection: str): + self._database = database + self._collection = collection + + @staticmethod + def from_proto( + storage_proto: SavedDatasetStorageProto, + ) -> "SavedDatasetMongoDBStorageNative": + config = json.loads(storage_proto.custom_storage.configuration) + return SavedDatasetMongoDBStorageNative( + database=config["database"], + collection=config["collection"], + ) + + def to_proto(self) -> SavedDatasetStorageProto: + return SavedDatasetStorageProto( + custom_storage=DataSourceProto.CustomSourceOptions( + configuration=json.dumps( + {"database": self._database, "collection": self._collection} + ).encode() + ) + ) + def to_data_source(self) -> DataSource: + return MongoDBSourceNative(name=self._collection) -def _fetch_documents( - client: Any, - database: str, - collection: str, - pipeline: List[Dict], -) -> List[Dict]: - """Execute an aggregation pipeline and return documents.""" - return list(client[database][collection].aggregate(pipeline)) + +# --------------------------------------------------------------------------- +# RetrievalJob +# --------------------------------------------------------------------------- class MongoDBNativeRetrievalJob(RetrievalJob): - """Retrieval job for native MongoDB offline store queries.""" + """Retrieval job for the native MongoDB offline store.""" def __init__( self, query_fn: Callable[[], pyarrow.Table], full_feature_names: bool, + config: RepoConfig, on_demand_feature_views: Optional[List[Any]] = None, metadata: Optional[RetrievalMetadata] = None, ): self._query_fn = query_fn self._full_feature_names = full_feature_names + self._config = config self._on_demand_feature_views = on_demand_feature_views or [] self._metadata = metadata @@ -346,114 +393,170 @@ def metadata(self) -> Optional[RetrievalMetadata]: def persist( self, - storage: Any, + storage: SavedDatasetStorage, allow_overwrite: bool = False, timeout: Optional[int] = None, ) -> None: - # TODO: Implement persist for native store - raise NotImplementedError("persist() not yet implemented for native store") + if not isinstance(storage, SavedDatasetMongoDBStorageNative): + raise ValueError( + f"MongoDBNativeRetrievalJob.persist expected " + f"SavedDatasetMongoDBStorageNative, got " + f"{type(storage).__name__!r}." + ) + if MongoClient is None: + raise FeastExtrasDependencyImportError( + "mongodb", "pymongo is not installed." + ) + db_name = storage._database or self._config.offline_store.database + coll_name = storage._collection + location = f"{db_name}.{coll_name}" + + client: Any = MongoClient( + self._config.offline_store.connection_string, + driver=DRIVER_METADATA, + tz_aware=True, + ) + try: + coll = client[db_name][coll_name] + if not allow_overwrite and coll.estimated_document_count() > 0: + raise SavedDatasetLocationAlreadyExists(location=location) + if allow_overwrite: + coll.drop() + records = self._to_arrow_internal().to_pylist() + if records: + coll.insert_many(records) + finally: + client.close() + + +# --------------------------------------------------------------------------- +# Module-level helpers +# --------------------------------------------------------------------------- + + +def _infer_python_type_str(value: Any) -> Optional[str]: + """Infer a Feast-compatible type string from a Python value.""" + if value is None: + return None + if isinstance(value, bool): + return "bool" + if isinstance(value, int): + return "int" + if isinstance(value, float): + return "float" + if isinstance(value, str): + return "str" + if isinstance(value, bytes): + return "bytes" + if isinstance(value, datetime): + return "datetime" + if isinstance(value, list): + if not value: + return "list[str]" + elem_type = _infer_python_type_str(value[0]) + return f"list[{elem_type}]" if elem_type else "list[str]" + return None def _serialize_entity_key_from_row( - row: pd.Series, join_keys: List[str], entity_key_serialization_version: int + row: pd.Series, + join_keys: List[str], + entity_key_serialization_version: int, + join_key_types: Optional[Dict[str, "ValueType"]] = None, ) -> bytes: - """Serialize entity key from a DataFrame row.""" + """Serialize an entity key from a DataFrame row. + + Args: + row: DataFrame row containing join key values. + join_keys: Names of the join key columns for this feature view. + entity_key_serialization_version: Version passed to serialize_entity_key. + join_key_types: Declared ValueType per join key, derived from + ``FeatureView.entity_columns``. Required for correct INT32 vs + INT64 serialization. Without it, all Python ``int`` values map + to ``int64_val``, silently mismatching stored INT32 entity keys. + """ entity_key = EntityKeyProto() for key in sorted(join_keys): entity_key.join_keys.append(key) value = row[key] val = ValueProto() - if isinstance(value, int): - val.int64_val = value - elif isinstance(value, str): - val.string_val = value - elif isinstance(value, float): - val.double_val = value + if hasattr(value, "item"): + # Unwrap numpy scalars (numpy 2.0 broke isinstance(np.int64, int)) + value = value.item() + declared_type = join_key_types.get(key) if join_key_types else None + if declared_type is not None: + if declared_type == ValueType.INT32: + val.int32_val = int(value) + elif declared_type == ValueType.INT64: + val.int64_val = int(value) + elif declared_type == ValueType.STRING: + val.string_val = str(value) + elif declared_type == ValueType.BYTES: + val.bytes_val = bytes(value) + elif declared_type == ValueType.UNIX_TIMESTAMP: + val.unix_timestamp_val = int(value) else: - val.string_val = str(value) + if isinstance(value, bool): + val.bool_val = value + elif isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + elif isinstance(value, float): + val.double_val = value + else: + val.string_val = str(value) entity_key.entity_values.append(val) return serialize_entity_key(entity_key, entity_key_serialization_version) -def _ttl_to_ms(fv: FeatureView) -> Optional[int]: - """Convert FeatureView TTL to milliseconds.""" - if fv.ttl is None: - return None - return int(fv.ttl.total_seconds() * 1000) - - -def _build_ttl_gte_expr(feature_views: List[FeatureView]) -> Optional[Dict[str, Any]]: - """Build a $gte expression with per-FV TTL using $switch. - - Returns a MongoDB expression that evaluates to: - event_timestamp >= (entity_timestamp - ttl_for_this_feature_view) +# --------------------------------------------------------------------------- +# Offline store +# --------------------------------------------------------------------------- - Each feature_view can have a different TTL, handled via $switch branches. - If no feature views have TTL, returns None (no filtering needed). - """ - branches = [] - - for fv in feature_views: - ttl_ms = _ttl_to_ms(fv) - if ttl_ms is None: - # No TTL for this FV - skip (effectively infinite history) - continue - - branches.append( - { - "case": {"$eq": ["$feature_view", fv.name]}, - "then": {"$subtract": ["$$ts", ttl_ms]}, - } - ) - # If no TTLs at all, no lower bound needed - if not branches: - return None - - return { - "$gte": [ - "$event_timestamp", - { - "$switch": { - "branches": branches, - # Default: no lower bound (for FVs without TTL) - "default": {"$literal": 0}, - } - }, - ] - } +class MongoDBOfflineStoreNative(OfflineStore): + """Atlas-first MongoDB offline store using a single shared collection. + All computation — sorting, grouping, TTL filtering, and the point-in-time + join — runs as a MongoDB aggregation pipeline on the Atlas cluster. The + client serializes entity keys, constructs the pipeline, and assembles the + final flat DataFrame from the matched feature documents returned by Atlas. -class MongoDBOfflineStoreNative(OfflineStore): - """Native MongoDB offline store using single-collection schema. - - All feature views share one collection (``feature_history``), with documents - containing: - - ``entity_id``: serialized entity key (bytes) - - ``feature_view``: field matching FeatureView name - - ``features``: subdocument with feature name/value pairs - - ``event_timestamp``: event time - - ``created_at``: ingestion time + Requires MongoDB 5.1+ for the ``$documents`` aggregation stage. """ - _index_initialized: bool = False - @staticmethod def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: - """Create recommended indexes on the feature_history collection.""" + """Create the compound index that covers all query patterns. + + The four-key index satisfies: + - pull_latest: sort {entity_id, event_timestamp DESC, created_at DESC} + after an equality match on feature_view. + - get_historical_features: $lookup subpipeline match on entity_id + (equality) + feature_view (equality) + event_timestamp (range), + then sort by event_timestamp DESC, created_at DESC. + """ collection = client[db_name][collection_name] + target_key = [ + ("entity_id", 1), + ("feature_view", 1), + ("event_timestamp", -1), + ("created_at", -1), + ] + existing = collection.index_information() + for idx_info in existing.values(): + if idx_info.get("key") == target_key: + return collection.create_index( - [ - ("entity_id", 1), - ("feature_view", 1), - ("event_timestamp", -1), - ], + target_key, name="entity_fv_ts_idx", + background=True, ) - @classmethod - def _get_client_and_ensure_indexes(cls, config: RepoConfig) -> Any: - """Get a MongoClient and ensure indexes exist (once per process).""" + @staticmethod + def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: + """Return a MongoClient, creating the compound index once per process.""" if MongoClient is None: raise FeastExtrasDependencyImportError( "mongodb", "pymongo is not installed." @@ -461,17 +564,133 @@ def _get_client_and_ensure_indexes(cls, config: RepoConfig) -> Any: client: Any = MongoClient( config.offline_store.connection_string, driver=DRIVER_METADATA ) - - if not cls._index_initialized: - cls._ensure_indexes( + cache_key = ( + config.offline_store.connection_string, + config.offline_store.database, + config.offline_store.collection, + ) + if cache_key not in _indexes_ensured: + MongoDBOfflineStoreNative._ensure_indexes( client, config.offline_store.database, config.offline_store.collection, ) - cls._index_initialized = True - + _indexes_ensured.add(cache_key) return client + # ------------------------------------------------------------------ + # Write path + # ------------------------------------------------------------------ + + @staticmethod + def offline_write_batch( + config: RepoConfig, + feature_view: FeatureView, + table: pyarrow.Table, + progress: Optional[Callable[[int], Any]], + ) -> None: + """Append a batch of feature observations to the feature_history collection. + + Each row in ``table`` becomes one document. Multiple documents for the + same (entity_id, feature_view, event_timestamp) are kept; corrections + win by having a later created_at. + """ + if not isinstance(feature_view.batch_source, MongoDBSourceNative): + raise ValueError( + f"MongoDBOfflineStoreNative.offline_write_batch expected a " + f"MongoDBSourceNative batch source, got " + f"{type(feature_view.batch_source).__name__!r}." + ) + + entity_key_version = config.entity_key_serialization_version + db_name = config.offline_store.database + collection_name = config.offline_store.collection + + # Derive join key names and declared types from entity_columns so that + # INT32 entities produce int32_val bytes, matching the read path. + join_key_types: Dict[str, ValueType] = { + feature_view.projection.join_key_map.get( + ec.name, ec.name + ): ec.dtype.to_value_type() + for ec in feature_view.entity_columns + } + join_keys = list(join_key_types.keys()) + + timestamp_field = feature_view.batch_source.timestamp_field + created_ts_col: Optional[str] = ( + feature_view.batch_source.created_timestamp_column or None + ) + reserved = set(join_keys) | {timestamp_field} + if created_ts_col: + reserved.add(created_ts_col) + feature_cols = [c for c in table.column_names if c not in reserved] + + df = table.to_pandas() + + for ts_col in [timestamp_field] + ([created_ts_col] if created_ts_col else []): + if ts_col in df.columns: + if not pd.api.types.is_datetime64_any_dtype(df[ts_col]): + df[ts_col] = pd.to_datetime(df[ts_col], utc=True) + elif df[ts_col].dt.tz is None: + df[ts_col] = df[ts_col].dt.tz_localize("UTC") + + df["_entity_id"] = df.apply( + lambda row: _serialize_entity_key_from_row( + row, join_keys, entity_key_version, join_key_types + ), + axis=1, + ) + + now = datetime.now(tz=timezone.utc) + docs = [] + for _, row in df.iterrows(): + features: Dict[str, Any] = {} + for col in feature_cols: + val = row[col] + if pd.isna(val): + continue + if hasattr(val, "item"): + val = val.item() + features[col] = val + + created_at = now + if created_ts_col and created_ts_col in df.columns: + ct = row[created_ts_col] + if not pd.isna(ct): + created_at = ( + ct.to_pydatetime() if hasattr(ct, "to_pydatetime") else ct + ) + + docs.append( + { + "entity_id": row["_entity_id"], + "feature_view": feature_view.name, + "features": features, + "event_timestamp": ( + row[timestamp_field].to_pydatetime() + if hasattr(row[timestamp_field], "to_pydatetime") + else row[timestamp_field] + ), + "created_at": created_at, + } + ) + + client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) + try: + coll = client[db_name][collection_name] + BATCH_SIZE = 10_000 + for i in range(0, len(docs), BATCH_SIZE): + batch = docs[i : i + BATCH_SIZE] + coll.insert_many(batch, ordered=False) + if progress: + progress(len(batch)) + finally: + client.close() + + # ------------------------------------------------------------------ + # Read path — materialization helpers + # ------------------------------------------------------------------ + @staticmethod def pull_latest_from_table_or_query( config: RepoConfig, @@ -483,6 +702,10 @@ def pull_latest_from_table_or_query( start_date: datetime, end_date: datetime, ) -> RetrievalJob: + """Return the most recent feature document per entity within [start, end]. + + Used by the materialization engine to populate the online store. + """ if not isinstance(data_source, MongoDBSourceNative): raise ValueError( f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " @@ -496,11 +719,9 @@ def pull_latest_from_table_or_query( db_name = config.offline_store.database collection = config.offline_store.collection feature_view_name = data_source.feature_view_name - start_utc = start_date.astimezone(tz=timezone.utc) end_utc = end_date.astimezone(tz=timezone.utc) - # Build projection to flatten features subdoc to top-level fields project_stage: Dict[str, Any] = { "_id": 0, "entity_id": "$doc.entity_id", @@ -511,7 +732,8 @@ def pull_latest_from_table_or_query( for feat in feature_name_columns: project_stage[feat] = f"$doc.features.{feat}" - # Build aggregation pipeline + # entity_id leads the sort so the compound index backs the entire stage. + # created_at tiebreaks corrections sharing the same event_timestamp. pipeline: List[Dict[str, Any]] = [ { "$match": { @@ -520,22 +742,18 @@ def pull_latest_from_table_or_query( } }, {"$sort": {"entity_id": 1, "event_timestamp": -1, "created_at": -1}}, - { - "$group": { - "_id": "$entity_id", - "doc": {"$first": "$$ROOT"}, - } - }, + {"$group": {"_id": "$entity_id", "doc": {"$first": "$$ROOT"}}}, {"$project": project_stage}, ] def _run() -> pyarrow.Table: client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) try: - docs = _fetch_documents(client, db_name, collection, pipeline) + docs = list( + client[db_name][collection].aggregate(pipeline, allowDiskUse=True) + ) if not docs: return pyarrow.Table.from_pydict({}) - df = pd.DataFrame(docs) if not df.empty and "event_timestamp" in df.columns: if df["event_timestamp"].dt.tz is None: @@ -546,7 +764,9 @@ def _run() -> pyarrow.Table: finally: client.close() - return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) + return MongoDBNativeRetrievalJob( + query_fn=_run, full_feature_names=False, config=config + ) @staticmethod def pull_all_from_table_or_query( @@ -559,6 +779,10 @@ def pull_all_from_table_or_query( start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, ) -> RetrievalJob: + """Return all feature documents in the given time range without deduplication. + + Used for bulk export and dataset generation. + """ if not isinstance(data_source, MongoDBSourceNative): raise ValueError( f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " @@ -573,7 +797,6 @@ def pull_all_from_table_or_query( collection = config.offline_store.collection feature_view_name = data_source.feature_view_name - # Build match filter: feature_view + optional time range match_filter: Dict[str, Any] = {"feature_view": feature_view_name} if start_date or end_date: ts_filter: Dict[str, Any] = {} @@ -583,8 +806,6 @@ def pull_all_from_table_or_query( ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) match_filter["event_timestamp"] = ts_filter - # Build projection: flatten features subdoc to top-level fields - # This uses $getField to extract each feature from the features subdoc project_stage: Dict[str, Any] = { "_id": 0, "entity_id": 1, @@ -595,7 +816,6 @@ def pull_all_from_table_or_query( for feat in feature_name_columns: project_stage[feat] = f"$features.{feat}" - # Simple range scan pipeline - no sorting for efficiency pipeline: List[Dict[str, Any]] = [ {"$match": match_filter}, {"$project": project_stage}, @@ -604,10 +824,11 @@ def pull_all_from_table_or_query( def _run() -> pyarrow.Table: client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) try: - docs = _fetch_documents(client, db_name, collection, pipeline) + docs = list( + client[db_name][collection].aggregate(pipeline, allowDiskUse=True) + ) if not docs: return pyarrow.Table.from_pydict({}) - df = pd.DataFrame(docs) if not df.empty and "event_timestamp" in df.columns: if df["event_timestamp"].dt.tz is None: @@ -618,7 +839,13 @@ def _run() -> pyarrow.Table: finally: client.close() - return MongoDBNativeRetrievalJob(query_fn=_run, full_feature_names=False) + return MongoDBNativeRetrievalJob( + query_fn=_run, full_feature_names=False, config=config + ) + + # ------------------------------------------------------------------ + # Read path — point-in-time join + # ------------------------------------------------------------------ @staticmethod def get_historical_features( @@ -630,6 +857,14 @@ def get_historical_features( project: str, full_feature_names: bool = False, ) -> RetrievalJob: + """Perform a point-in-time join entirely on the Atlas cluster. + + The entity DataFrame is injected into the aggregation pipeline via + ``$documents`` (MongoDB 5.1+). One ``$lookup`` stage per feature view + performs the correlated PIT join on the server; no temp collection is + created. Entity keys are serialized per-FV so that each feature view's + documents are matched using only that view's own join keys. + """ if isinstance(entity_df, str): raise ValueError( "MongoDBOfflineStoreNative does not support SQL entity_df strings. " @@ -647,17 +882,37 @@ def get_historical_features( entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) - # Map "feature_view:feature" refs → {fv_name: [feature, ...]} - fv_to_features: Dict[str, List[str]] = {} + # Map "feature_view:feature_name" refs → {fv_name: [feature, ...]} + fv_to_features: Dict[str, List[str]] = defaultdict(list) for ref in feature_refs: fv_name, feat_name = ref.split(":", 1) - fv_to_features.setdefault(fv_name, []).append(feat_name) + fv_to_features[fv_name].append(feat_name) - fv_names = list(fv_to_features.keys()) + fv_by_name = {fv.name: fv for fv in feature_views} - # Build per-FV TTL expression using $switch - relevant_fvs = [fv for fv in feature_views if fv.name in fv_to_features] - ttl_expr = _build_ttl_gte_expr(relevant_fvs) + # Per-FV join keys: using only each FV's own join keys is the critical + # invariant. A driver_stats document is keyed by serialize({driver_id: + # 1001}); including customer_id in the key would produce bytes that + # never match any stored document. + fv_join_keys_by_name: Dict[str, List[str]] = { + fv.name: list(get_expected_join_keys(project, [fv], registry)) + for fv in feature_views + } + + # Declared ValueType per join key — required for INT32 vs INT64 + # correctness (see _serialize_entity_key_from_row docstring). + fv_join_key_types_by_name: Dict[str, Dict[str, ValueType]] = { + fv.name: { + fv.projection.join_key_map.get( + ec.name, ec.name + ): ec.dtype.to_value_type() + for ec in fv.entity_columns + } + for fv in feature_views + } + + # Original entity columns (everything except the timestamp). + entity_columns = [c for c in entity_df.columns if c != event_timestamp_col] def _run() -> pyarrow.Table: if MongoClient is None: @@ -665,130 +920,191 @@ def _run() -> pyarrow.Table: "mongodb", "pymongo is not installed." ) - # Prepare entity_df: ensure timestamps are UTC and serialize entity keys - result = entity_df.copy() - if result[event_timestamp_col].dt.tz is None: - result[event_timestamp_col] = pd.to_datetime( - result[event_timestamp_col], utc=True - ) - - # Get join keys (all columns except event_timestamp) - entity_columns = [c for c in result.columns if c != event_timestamp_col] + # Work on a copy with a guaranteed 0-based integer index so that + # _row_idx stored in $documents == iloc position in result. + work = entity_df.copy() + work = work.reset_index(drop=True) - # Serialize entity keys to bytes (same format as online store) - result["_entity_id"] = result.apply( - lambda row: _serialize_entity_key_from_row( - row, entity_columns, entity_key_version - ), - axis=1, - ) - - # Build temp collection documents - temp_docs = [] - for _, row in result.iterrows(): - temp_docs.append( - { - "entity_id": row["_entity_id"], - "event_timestamp": row[event_timestamp_col], - "_row_idx": _, # Preserve original order - } + if not pd.api.types.is_datetime64_any_dtype(work[event_timestamp_col]): + work[event_timestamp_col] = pd.to_datetime( + work[event_timestamp_col], utc=True + ) + elif work[event_timestamp_col].dt.tz is None: + work[event_timestamp_col] = work[event_timestamp_col].dt.tz_localize( + "UTC" ) - # Create temporary collection for query - temp_collection_name = f"tmp_entity_df_{uuid.uuid4().hex[:12]}" + # Serialize per-FV entity keys once for the whole DataFrame. + # Column names use a prefix unlikely to clash with feature names. + for fv_name in fv_to_features: + join_keys = fv_join_keys_by_name[fv_name] + join_key_types = fv_join_key_types_by_name[fv_name] + work[f"__eid__{fv_name}"] = work.apply( + lambda row, jk=join_keys, jkt=join_key_types: ( + _serialize_entity_key_from_row(row, jk, entity_key_version, jkt) + ), + axis=1, + ) client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) try: db = client[db_name] - temp_collection = db[temp_collection_name] - temp_collection.insert_many(temp_docs) - - # Build $lookup subpipeline with PIT join logic - # Match: entity_id, feature_view in list, event_timestamp <= entity.ts - match_conditions: List[Dict[str, Any]] = [ - {"$eq": ["$entity_id", "$$entity_id"]}, - {"$in": ["$feature_view", fv_names]}, - {"$lte": ["$event_timestamp", "$$ts"]}, - ] - # Add per-FV TTL filter using $switch - if ttl_expr is not None: - match_conditions.append(ttl_expr) - - lookup_pipeline: List[Dict[str, Any]] = [ - {"$match": {"$expr": {"$and": match_conditions}}}, - {"$sort": {"feature_view": 1, "event_timestamp": -1}}, - { - "$group": { - "_id": "$feature_view", - "doc": {"$first": "$$ROOT"}, - } - }, - ] - - # Main aggregation pipeline - pipeline: List[Dict[str, Any]] = [ - { - "$lookup": { - "from": feature_collection, - "let": { - "entity_id": "$entity_id", - "ts": "$event_timestamp", - }, - "pipeline": lookup_pipeline, - "as": "feature_rows", + all_output_rows: List[Dict[str, Any]] = [] + + for chunk_start in range(0, len(work), _CHUNK_SIZE): + chunk = work.iloc[chunk_start : chunk_start + _CHUNK_SIZE] + + # Build the $documents array. Each entry carries: + # _row_idx – position in work (used to recover entity cols) + # _ts – entity row's event_timestamp (PIT upper bound) + # _entity_ids – per-FV serialized key bytes + documents: List[Dict[str, Any]] = [] + for row_idx, row in chunk.iterrows(): + ts = row[event_timestamp_col] + if hasattr(ts, "to_pydatetime"): + ts = ts.to_pydatetime() + documents.append( + { + "_row_idx": int(row_idx), + "_ts": ts, + "_entity_ids": { + fv_name: row[f"__eid__{fv_name}"] + for fv_name in fv_to_features + }, + } + ) + + # Build the pipeline: $documents + one $lookup per FV. + # + # Each $lookup: + # let eid = $_entity_ids. (Binary bytes) + # ts = $_ts (datetime) + # + # subpipeline: + # $match entity_id == $$eid + # feature_view == (equality → index) + # event_timestamp <= $$ts (PIT upper bound) + # event_timestamp >= $$ts - ttl_ms (optional) + # $sort event_timestamp DESC, created_at DESC + # $limit 1 (most recent doc) + # $project features only (reduce transfer) + pipeline: List[Dict[str, Any]] = [{"$documents": documents}] + + for fv_name, _features in fv_to_features.items(): + fv = fv_by_name.get(fv_name) + match_exprs: List[Dict[str, Any]] = [ + {"$eq": ["$entity_id", "$$eid"]}, + {"$eq": ["$feature_view", fv_name]}, + {"$lte": ["$event_timestamp", "$$ts"]}, + ] + if fv and fv.ttl: + ttl_ms = int(fv.ttl.total_seconds() * 1000) + match_exprs.append( + { + "$gte": [ + "$event_timestamp", + {"$subtract": ["$$ts", ttl_ms]}, + ] + } + ) + + pipeline.append( + { + "$lookup": { + "from": feature_collection, + "let": { + # Access the per-FV key from the + # _entity_ids subdocument. + "eid": f"$_entity_ids.{fv_name}", + "ts": "$_ts", + }, + "pipeline": [ + {"$match": {"$expr": {"$and": match_exprs}}}, + { + "$sort": { + "event_timestamp": -1, + "created_at": -1, + } + }, + {"$limit": 1}, + # Return only features — drop entity_id, + # feature_view, timestamps, and _id to + # minimise data transferred from Atlas. + { + "$project": { + "_id": 0, + "features": 1, + } + }, + ], + "as": f"_match_{fv_name}", + } + } + ) + + # Drop _entity_ids before results cross the network. + pipeline.append( + { + "$project": { + "_id": 0, + "_row_idx": 1, + **{ + f"_match_{fv_name}": 1 for fv_name in fv_to_features + }, + } } - }, - {"$sort": {"_row_idx": 1}}, # Preserve original order - ] + ) - docs = list(temp_collection.aggregate(pipeline)) + # $documents pipelines run against the database, not a + # specific collection. + matched_docs = list(db.aggregate(pipeline, allowDiskUse=True)) + + # Assemble flat output rows from the matched documents. + for doc in matched_docs: + row_idx = doc["_row_idx"] + orig = work.iloc[row_idx] + + out: Dict[str, Any] = {} + for col in entity_columns: + val = orig[col] + if hasattr(val, "item"): + val = val.item() + out[col] = val + out[event_timestamp_col] = orig[event_timestamp_col] + out["_row_idx"] = row_idx + + for fv_name, feats in fv_to_features.items(): + matches = doc.get(f"_match_{fv_name}", []) + feat_doc = matches[0] if matches else None + feat_values = ( + feat_doc.get("features", {}) if feat_doc else {} + ) + for feat in feats: + col_name = ( + f"{fv_name}__{feat}" if full_feature_names else feat + ) + out[col_name] = feat_values.get(feat) + + all_output_rows.append(out) finally: - # Cleanup temp collection - client[db_name][temp_collection_name].drop() client.close() - if not docs: - return pyarrow.Table.from_pydict({}) - - # Build result DataFrame - result = result.reset_index(drop=True) - rows = [] - for doc in docs: - # Start with entity columns from original entity_df - row_idx = doc["_row_idx"] - row = result.iloc[row_idx][ - entity_columns + [event_timestamp_col] - ].to_dict() - - # Extract features from each feature_view's matched doc - feature_rows_by_fv = { - fr["_id"]: fr["doc"] for fr in doc.get("feature_rows", []) - } - - # Extract features from each feature_view's matched doc - # TTL is already applied server-side via $switch expression - for fv_name, features in fv_to_features.items(): - fv_doc = feature_rows_by_fv.get(fv_name) + # Restore original entity_df ordering. + all_output_rows.sort(key=lambda r: r["_row_idx"]) + result_df = pd.DataFrame(all_output_rows) + result_df = result_df.drop(columns=["_row_idx"], errors="ignore") - for feat in features: - col_name = f"{fv_name}__{feat}" if full_feature_names else feat - if fv_doc is None: - row[col_name] = None - else: - row[col_name] = fv_doc.get("features", {}).get(feat) - - rows.append(row) - - result_df = pd.DataFrame(rows) if not result_df.empty and event_timestamp_col in result_df.columns: if result_df[event_timestamp_col].dt.tz is None: result_df[event_timestamp_col] = pd.to_datetime( result_df[event_timestamp_col], utc=True ) + return pyarrow.Table.from_pandas(result_df, preserve_index=False) return MongoDBNativeRetrievalJob( query_fn=_run, full_feature_names=full_feature_names, + config=config, ) From 47f10408cdaf8ee43d0d839200d87e90e30481cc Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 17 Apr 2026 18:15:42 -0400 Subject: [PATCH 47/76] Add unit tests for MongoDBOfflineStoreNative Mirrors test_one.py for the $documents + $lookup implementation. Includes test_heterogeneous_join_keys which specifically exercises the per-FV entity key fix (driver_stats and customer_stats keyed by different join keys in the same entity_df). Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .../mongodb_offline_store/test_native.py | 1111 +++++++++++++++++ 1 file changed, 1111 insertions(+) create mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_native.py diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_native.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_native.py new file mode 100644 index 00000000000..6946dc517b7 --- /dev/null +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_native.py @@ -0,0 +1,1111 @@ +""" +Unit tests for the Atlas-first MongoDB native offline store. + +Uses the single-collection schema (all FVs share ``feature_history``, +discriminated by ``feature_view`` field) and performs point-in-time joins +entirely via ``$documents`` + ``$lookup`` aggregation pipelines. + +Requires MongoDB 5.1+ (Atlas M10+, or self-managed). The testcontainer +uses ``mongo:latest`` which satisfies this requirement. + +Docker-dependent tests are marked with ``@_requires_docker`` and are skipped +when Docker is unavailable. +""" + +from datetime import datetime, timedelta +from typing import Dict, Optional +from unittest.mock import MagicMock + +import pandas as pd +import pyarrow +import pytest +import pytz + +pytest.importorskip("pymongo") + +from pymongo import MongoClient +from testcontainers.mongodb import MongoDbContainer + +from feast import Entity, FeatureView, Field +from feast.errors import SavedDatasetLocationAlreadyExists +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( + MongoDBOfflineStoreNative, + MongoDBOfflineStoreNativeConfig, + MongoDBSourceNative, + SavedDatasetMongoDBStorageNative, +) +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import RepoConfig +from feast.types import Float64, Int32, Int64, String +from feast.value_type import ValueType + +# --------------------------------------------------------------------------- +# Docker guard +# --------------------------------------------------------------------------- + +docker_available = False +try: + import docker + + try: + _docker_client = docker.from_env() + _docker_client.ping() + docker_available = True + except Exception: + pass +except ImportError: + pass + +_requires_docker = pytest.mark.skipif( + not docker_available, + reason="Docker is not available or not running.", +) + +ENTITY_KEY_VERSION = 3 + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _make_entity_id( + join_keys: dict, + value_types: Optional[Dict[str, ValueType]] = None, +) -> bytes: + """Serialize an entity key from a plain dict, mirroring _serialize_entity_key_from_row. + + Args: + join_keys: Mapping of join key name → value. + value_types: Optional declared ValueType per key. When provided the + correct proto field is used (e.g. INT32 → int32_val). Omit to + get int64_val/string_val inference. + """ + entity_key = EntityKeyProto() + for key in sorted(join_keys.keys()): + entity_key.join_keys.append(key) + val = ValueProto() + value = join_keys[key] + declared_type = value_types.get(key) if value_types else None + if declared_type == ValueType.INT32: + val.int32_val = int(value) + elif declared_type == ValueType.INT64: + val.int64_val = int(value) + elif declared_type == ValueType.STRING: + val.string_val = str(value) + elif declared_type == ValueType.BYTES: + val.bytes_val = bytes(value) + elif declared_type == ValueType.UNIX_TIMESTAMP: + val.unix_timestamp_val = int(value) + else: + if isinstance(value, bool): + val.bool_val = value + elif isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + else: + val.string_val = str(value) + entity_key.entity_values.append(val) + return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +@pytest.fixture(scope="module") +def mongodb_container(): + container = MongoDbContainer( + "mongo:latest", + username="test", + password="test", # pragma: allowlist secret + ).with_exposed_ports(27017) + container.start() + yield container + container.stop() + + +@pytest.fixture +def mongodb_connection_string(mongodb_container) -> str: + port = mongodb_container.get_exposed_port(27017) + return f"mongodb://test:test@localhost:{port}" # pragma: allowlist secret + + +@pytest.fixture +def repo_config(mongodb_connection_string: str) -> RepoConfig: + return RepoConfig( + project="test_project", + registry="memory://", + provider="local", + offline_store=MongoDBOfflineStoreNativeConfig( + connection_string=mongodb_connection_string, + database="feast_test", + collection="feature_history", + ), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + + +@pytest.fixture +def sample_data(mongodb_connection_string: str) -> datetime: + """Insert driver_stats documents using the single-collection schema.""" + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + coll = db["feature_history"] + coll.drop() + + now = datetime.now(tz=pytz.UTC) + coll.insert_many( + [ + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats", + "features": {"conv_rate": 0.5, "acc_rate": 0.9}, + "event_timestamp": now - timedelta(hours=2), + "created_at": now - timedelta(hours=2), + }, + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats", + "features": {"conv_rate": 0.6, "acc_rate": 0.85}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats", + "features": {"conv_rate": 0.7, "acc_rate": 0.8}, + "event_timestamp": now, + "created_at": now, + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats", + "features": {"conv_rate": 0.3, "acc_rate": 0.95}, + "event_timestamp": now - timedelta(hours=2), + "created_at": now - timedelta(hours=2), + }, + ] + ) + client.close() + return now + + +@pytest.fixture +def driver_source() -> MongoDBSourceNative: + return MongoDBSourceNative( + name="driver_stats", + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + ) + + +@pytest.fixture +def driver_fv(driver_source: MongoDBSourceNative) -> FeatureView: + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + return FeatureView( + name="driver_stats", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + Field(name="acc_rate", dtype=Float64), + ], + source=driver_source, + ttl=timedelta(days=1), + ) + + +# --------------------------------------------------------------------------- +# Tests — pull_latest_from_table_or_query +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_pull_latest_from_table_or_query( + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceNative +) -> None: + """pull_latest returns one row per entity, taking the most recent document.""" + now = sample_data + job = MongoDBOfflineStoreNative.pull_latest_from_table_or_query( + config=repo_config, + data_source=driver_source, + join_key_columns=["driver_id"], + feature_name_columns=["conv_rate", "acc_rate"], + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + start_date=now - timedelta(days=1), + end_date=now + timedelta(hours=1), + ) + + df = job.to_df() + assert isinstance(df, pd.DataFrame) + assert len(df) == 2 # two unique entity_ids + + conv_rates = sorted(df["conv_rate"].tolist()) + assert conv_rates[0] == pytest.approx(0.3) # driver 2's only value + assert conv_rates[1] == pytest.approx(0.7) # driver 1's latest + + +# --------------------------------------------------------------------------- +# Tests — pull_all_from_table_or_query +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_pull_all_from_table_or_query( + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceNative +) -> None: + """pull_all returns every document in the time range without deduplication.""" + now = sample_data + job = MongoDBOfflineStoreNative.pull_all_from_table_or_query( + config=repo_config, + data_source=driver_source, + join_key_columns=["driver_id"], + feature_name_columns=["conv_rate", "acc_rate"], + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + start_date=now - timedelta(hours=1, minutes=30), + end_date=now + timedelta(hours=1), + ) + + df = job.to_df() + assert isinstance(df, pd.DataFrame) + # 2 rows: driver 1 from 1 hr ago and driver 1 from now + # Excludes: driver 1 from 2 hrs ago, driver 2 from 2 hrs ago + assert len(df) == 2 + + +# --------------------------------------------------------------------------- +# Tests — get_historical_features (PIT join) +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_get_historical_features_pit_join( + repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView +) -> None: + """PIT join returns the most recent feature value at or before each entity row's timestamp.""" + now = sample_data + + entity_df = pd.DataFrame( + { + "driver_id": [1, 1, 2], + "event_timestamp": [ + now - timedelta(hours=1, minutes=30), # → conv_rate=0.5 + now - timedelta(minutes=30), # → conv_rate=0.6 + now - timedelta(hours=1), # → conv_rate=0.3 + ], + } + ) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df() + assert isinstance(result_df, pd.DataFrame) + assert len(result_df) == 3 + + result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( + drop=True + ) + + # driver 1, 1.5 hrs ago → feature row from 2 hrs ago + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) + # driver 1, 30 min ago → feature row from 1 hr ago + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) + # driver 2, 1 hr ago → feature row from 2 hrs ago + assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) + + +@_requires_docker +def test_ttl_excludes_stale_features( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Documents older than TTL must come back as NULL, not the stale value.""" + client: MongoClient = MongoClient(mongodb_connection_string) + now = datetime.now(tz=pytz.UTC) + coll = client["feast_test"]["feature_history"] + coll.insert_many( + [ + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_ttl", + "features": {"conv_rate": 0.9}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats_ttl", + "features": {"conv_rate": 0.5}, + "event_timestamp": now - timedelta(days=2), # stale + "created_at": now - timedelta(days=2), + }, + ] + ) + client.close() + + source = MongoDBSourceNative(name="driver_stats_ttl") + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + fv = FeatureView( + name="driver_stats_ttl", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["driver_stats_ttl:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + + # driver 1: fresh → value present + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.9) + # driver 2: stale → NULL + assert pd.isna(result_df.loc[1, "conv_rate"]) + + +@_requires_docker +def test_multiple_feature_views( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Features from two FVs with the same join key are joined correctly.""" + client: MongoClient = MongoClient(mongodb_connection_string) + now = datetime.now(tz=pytz.UTC) + coll = client["feast_test"]["feature_history"] + coll.insert_many( + [ + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_multi", + "features": {"rating": 4.8}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats_multi", + "features": {"rating": 4.5}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "vehicle_stats_multi", + "features": {"vehicle_age": 2, "mileage": 50000}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "vehicle_stats_multi", + "features": {"vehicle_age": 5, "mileage": 120000}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + ] + ) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + driver_fv = FeatureView( + name="driver_stats_multi", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="rating", dtype=Float64), + ], + source=MongoDBSourceNative(name="driver_stats_multi"), + ttl=timedelta(days=1), + ) + vehicle_fv = FeatureView( + name="vehicle_stats_multi", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="vehicle_age", dtype=Int64), + Field(name="mileage", dtype=Int64), + ], + source=MongoDBSourceNative(name="vehicle_stats_multi"), + ttl=timedelta(days=1), + ) + + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv, vehicle_fv], + feature_refs=[ + "driver_stats_multi:rating", + "vehicle_stats_multi:vehicle_age", + "vehicle_stats_multi:mileage", + ], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + assert len(result_df) == 2 + assert {"driver_id", "rating", "vehicle_age", "mileage"}.issubset(result_df.columns) + + assert result_df.loc[0, "rating"] == pytest.approx(4.8) + assert result_df.loc[0, "vehicle_age"] == 2 + assert result_df.loc[0, "mileage"] == 50000 + assert result_df.loc[1, "rating"] == pytest.approx(4.5) + assert result_df.loc[1, "vehicle_age"] == 5 + assert result_df.loc[1, "mileage"] == 120000 + + +@_requires_docker +def test_heterogeneous_join_keys( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """FVs with different join keys must each match only their own documents. + + This is the core correctness invariant of the per-FV entity key design. + driver_stats documents are keyed by serialize({driver_id: X}). + customer_stats documents are keyed by serialize({customer_id: Y}). + The entity_df has both columns. + + A naive implementation would serialize {customer_id: Y, driver_id: X} + (union of all entity columns) and query that single key against both FVs — + producing bytes that match neither stored collection. The correct + implementation serializes each FV's key independently and issues a separate + $lookup per FV. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + now = datetime.now(tz=pytz.UTC) + coll = client["feast_test"]["feature_history"] + coll.insert_many( + [ + # driver_stats: keyed by driver_id only + { + "entity_id": _make_entity_id({"driver_id": 10}), + "feature_view": "driver_stats_het", + "features": {"conv_rate": 0.72}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"driver_id": 20}), + "feature_view": "driver_stats_het", + "features": {"conv_rate": 0.55}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + # customer_stats: keyed by customer_id only + { + "entity_id": _make_entity_id({"customer_id": 100}), + "feature_view": "customer_stats_het", + "features": {"lifetime_spend": 1500.0}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"customer_id": 200}), + "feature_view": "customer_stats_het", + "features": {"lifetime_spend": 800.0}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + ] + ) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + customer_entity = Entity( + name="customer_id", join_keys=["customer_id"], value_type=ValueType.INT64 + ) + + driver_fv = FeatureView( + name="driver_stats_het", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=MongoDBSourceNative(name="driver_stats_het"), + ttl=timedelta(days=1), + ) + customer_fv = FeatureView( + name="customer_stats_het", + entities=[customer_entity], + schema=[ + Field(name="customer_id", dtype=Int64), + Field(name="lifetime_spend", dtype=Float64), + ], + source=MongoDBSourceNative(name="customer_stats_het"), + ttl=timedelta(days=1), + ) + + # entity_df carries both join keys — exactly the scenario the union-key + # bug would break. + entity_df = pd.DataFrame( + { + "driver_id": [10, 20], + "customer_id": [100, 200], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv, customer_fv], + feature_refs=[ + "driver_stats_het:conv_rate", + "customer_stats_het:lifetime_spend", + ], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + assert len(result_df) == 2 + + # If the union-key bug were present, both columns would be NULL. + assert result_df["conv_rate"].notna().all(), ( + "conv_rate is null — union join key serialization would include " + "customer_id in the driver_stats entity key, producing bytes that " + "never match stored documents." + ) + assert result_df["lifetime_spend"].notna().all(), ( + "lifetime_spend is null — union join key serialization would include " + "driver_id in the customer_stats entity key." + ) + + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.72) + assert result_df.loc[0, "lifetime_spend"] == pytest.approx(1500.0) + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.55) + assert result_df.loc[1, "lifetime_spend"] == pytest.approx(800.0) + + +@_requires_docker +def test_multiple_feature_views_overlapping_feature_names( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Three FVs all defining a feature named 'score' must not collide under full_feature_names=True.""" + client: MongoClient = MongoClient(mongodb_connection_string) + now = datetime.now(tz=pytz.UTC) + coll = client["feast_test"]["feature_history"] + + docs = [] + for fv_name, base in [("fv_ol_a", 1.0), ("fv_ol_b", 2.0), ("fv_ol_c", 3.0)]: + for driver_id in [1, 2]: + docs.append( + { + "entity_id": _make_entity_id({"driver_id": driver_id}), + "feature_view": fv_name, + "features": {"score": base + driver_id * 0.1}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + } + ) + coll.insert_many(docs) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + fvs = [] + feature_refs = [] + for fv_name in ["fv_ol_a", "fv_ol_b", "fv_ol_c"]: + fv = FeatureView( + name=fv_name, + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="score", dtype=Float64), + ], + source=MongoDBSourceNative(name=fv_name), + ttl=timedelta(days=1), + ) + fvs.append(fv) + feature_refs.append(f"{fv_name}:score") + + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=fvs, + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=True, + ) + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + + assert len(result_df) == 2 + assert "fv_ol_a__score" in result_df.columns + assert "fv_ol_b__score" in result_df.columns + assert "fv_ol_c__score" in result_df.columns + + assert result_df.loc[0, "fv_ol_a__score"] == pytest.approx(1.1) + assert result_df.loc[0, "fv_ol_b__score"] == pytest.approx(2.1) + assert result_df.loc[0, "fv_ol_c__score"] == pytest.approx(3.1) + assert result_df.loc[1, "fv_ol_a__score"] == pytest.approx(1.2) + assert result_df.loc[1, "fv_ol_b__score"] == pytest.approx(2.2) + assert result_df.loc[1, "fv_ol_c__score"] == pytest.approx(3.2) + + +@_requires_docker +def test_compound_join_keys( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Compound (multi-column) join keys are serialized and matched correctly.""" + client: MongoClient = MongoClient(mongodb_connection_string) + now = datetime.now(tz=pytz.UTC) + coll = client["feast_test"]["feature_history"] + coll.insert_many( + [ + { + "entity_id": _make_entity_id({"user_id": 1, "device_id": "mobile"}), + "feature_view": "user_device_features", + "features": {"app_opens": 50}, + "event_timestamp": now - timedelta(hours=2), + "created_at": now - timedelta(hours=2), + }, + { + "entity_id": _make_entity_id({"user_id": 1, "device_id": "mobile"}), + "feature_view": "user_device_features", + "features": {"app_opens": 55}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"user_id": 1, "device_id": "desktop"}), + "feature_view": "user_device_features", + "features": {"app_opens": 10}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"user_id": 2, "device_id": "tablet"}), + "feature_view": "user_device_features", + "features": {"app_opens": 25}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + ] + ) + client.close() + + source = MongoDBSourceNative(name="user_device_features") + user_entity = Entity( + name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 + ) + device_entity = Entity( + name="device_id", join_keys=["device_id"], value_type=ValueType.STRING + ) + fv = FeatureView( + name="user_device_features", + entities=[user_entity, device_entity], + schema=[ + Field(name="user_id", dtype=Int64), + Field(name="device_id", dtype=String), + Field(name="app_opens", dtype=Int64), + ], + source=source, + ttl=timedelta(days=1), + ) + + # pull_latest: one row per unique (user_id, device_id) + job = MongoDBOfflineStoreNative.pull_latest_from_table_or_query( + config=repo_config, + data_source=source, + join_key_columns=["user_id", "device_id"], + feature_name_columns=["app_opens"], + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + start_date=now - timedelta(days=1), + end_date=now + timedelta(hours=1), + ) + df = job.to_df() + assert len(df) == 3 + assert set(df["app_opens"].tolist()) == {55, 10, 25} + + # get_historical_features with compound keys + entity_df = pd.DataFrame( + { + "user_id": [1, 1, 2], + "device_id": ["mobile", "desktop", "tablet"], + "event_timestamp": [now, now, now], + } + ) + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["user_device_features:app_opens"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + result_df = job.to_df().sort_values(["user_id", "device_id"]).reset_index(drop=True) + assert len(result_df) == 3 + assert result_df.loc[0, "app_opens"] == 10 # user 1, desktop + assert result_df.loc[1, "app_opens"] == 55 # user 1, mobile (latest) + assert result_df.loc[2, "app_opens"] == 25 # user 2, tablet + + +@_requires_docker +def test_entity_df_with_extra_columns( + repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView +) -> None: + """Extra (label) columns in entity_df must not corrupt entity key serialization. + + The native store derives join keys from fv.entity_columns, not from all + non-timestamp columns in the entity_df. A label column such as + ``trip_success`` must pass through unchanged and must not enter the key. + """ + now = sample_data + + entity_df = pd.DataFrame( + { + "driver_id": [1, 1, 2], + "event_timestamp": [ + now - timedelta(hours=1, minutes=30), + now - timedelta(minutes=30), + now - timedelta(hours=1), + ], + "trip_success": [1, 0, 1], # label — must not affect entity key + } + ) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df() + assert len(result_df) == 3 + assert "trip_success" in result_df.columns + assert sorted(result_df["trip_success"].tolist()) == [0, 1, 1] + assert result_df["conv_rate"].notna().all(), ( + "conv_rate is null — label column was likely included in the entity key." + ) + + result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( + drop=True + ) + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) + assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) + + +# --------------------------------------------------------------------------- +# Tests — INT32 entity key round-trip +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_int32_entity_key( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """INT32 entity keys round-trip through the correct proto field (int32_val). + + Without the declared-type fix, Python ``int`` always maps to int64_val + (8 bytes), silently mismatching documents stored with int32_val (4 bytes). + """ + client: MongoClient = MongoClient(mongodb_connection_string) + now = datetime.now(tz=pytz.UTC) + int32_types = {"order_id": ValueType.INT32} + client["feast_test"]["feature_history"].insert_many( + [ + { + "entity_id": _make_entity_id({"order_id": 1}, int32_types), + "feature_view": "order_features", + "features": {"amount": 100.0}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + { + "entity_id": _make_entity_id({"order_id": 2}, int32_types), + "feature_view": "order_features", + "features": {"amount": 200.0}, + "event_timestamp": now - timedelta(hours=1), + "created_at": now - timedelta(hours=1), + }, + ] + ) + client.close() + + order_entity = Entity( + name="order_id", join_keys=["order_id"], value_type=ValueType.INT32 + ) + fv = FeatureView( + name="order_features", + entities=[order_entity], + schema=[ + Field(name="order_id", dtype=Int32), + Field(name="amount", dtype=Float64), + ], + source=MongoDBSourceNative(name="order_features"), + ttl=timedelta(days=1), + ) + + entity_df = pd.DataFrame({"order_id": [1, 2], "event_timestamp": [now, now]}) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["order_features:amount"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("order_id").reset_index(drop=True) + assert len(result_df) == 2 + assert result_df["amount"].notna().all(), ( + "amount is null — INT32 entity key was serialized as INT64, producing " + "bytes that do not match the stored INT32 documents." + ) + assert result_df.loc[0, "amount"] == pytest.approx(100.0) + assert result_df.loc[1, "amount"] == pytest.approx(200.0) + + +# --------------------------------------------------------------------------- +# Tests — offline_write_batch round-trip +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_offline_write_batch_round_trip( + repo_config: RepoConfig, + mongodb_connection_string: str, + driver_fv: FeatureView, +) -> None: + """offline_write_batch writes documents that get_historical_features can read back.""" + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"]["feature_history"].drop() + client.close() + + now = datetime.now(tz=pytz.UTC) + df = pd.DataFrame( + { + "driver_id": [1, 2], + "conv_rate": [0.8, 0.6], + "acc_rate": [0.9, 0.7], + "event_timestamp": [now - timedelta(hours=1), now - timedelta(hours=1)], + "created_at": [now, now], + } + ) + table = pyarrow.Table.from_pandas(df) + + MongoDBOfflineStoreNative.offline_write_batch( + config=repo_config, + feature_view=driver_fv, + table=table, + progress=None, + ) + + # Verify document structure + client = MongoClient(mongodb_connection_string) + docs = list( + client["feast_test"]["feature_history"].find( + {"feature_view": "driver_stats"}, {"_id": 0} + ) + ) + client.close() + + assert len(docs) == 2 + doc = docs[0] + assert isinstance(doc["entity_id"], bytes), "entity_id must be serialized bytes" + assert doc["feature_view"] == "driver_stats" + assert set(doc["features"].keys()) == {"conv_rate", "acc_rate"} + assert "event_timestamp" in doc + assert "created_at" in doc + + # Verify round-trip + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + assert len(result_df) == 2 + assert result_df["conv_rate"].notna().all(), ( + "conv_rate is null — offline_write_batch and get_historical_features " + "are producing different entity_id bytes." + ) + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.8) + assert result_df.loc[0, "acc_rate"] == pytest.approx(0.9) + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) + assert result_df.loc[1, "acc_rate"] == pytest.approx(0.7) + + +# --------------------------------------------------------------------------- +# Tests — persist() +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_persist_writes_flat_result( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist() writes the flat joined DataFrame to the named destination collection.""" + now = sample_data + dest = "saved_ds_native_basic" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client.close() + + entity_df = pd.DataFrame( + {"driver_id": [1, 2], "event_timestamp": [now, now - timedelta(hours=2)]} + ) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + job.persist( + SavedDatasetMongoDBStorageNative(database="feast_test", collection=dest) + ) + + client = MongoClient(mongodb_connection_string) + try: + docs = list(client["feast_test"][dest].find({}, {"_id": 0})) + finally: + client.close() + + assert len(docs) == 2 + assert {"driver_id", "conv_rate", "acc_rate", "event_timestamp"}.issubset( + docs[0].keys() + ) + + +@_requires_docker +def test_persist_raises_if_collection_exists( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist() raises SavedDatasetLocationAlreadyExists when destination is non-empty.""" + now = sample_data + dest = "saved_ds_native_no_overwrite" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client["feast_test"][dest].insert_one({"placeholder": True}) + client.close() + + entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + with pytest.raises(SavedDatasetLocationAlreadyExists): + job.persist( + SavedDatasetMongoDBStorageNative(database="feast_test", collection=dest), + allow_overwrite=False, + ) + + +@_requires_docker +def test_persist_allow_overwrite( + repo_config: RepoConfig, + sample_data: datetime, + driver_fv: FeatureView, + mongodb_connection_string: str, +) -> None: + """persist(allow_overwrite=True) replaces any existing collection contents.""" + now = sample_data + dest = "saved_ds_native_overwrite" + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"][dest].drop() + client["feast_test"][dest].insert_many([{"stale": True}, {"stale": True}]) + client.close() + + entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) + + job = MongoDBOfflineStoreNative.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + job.persist( + SavedDatasetMongoDBStorageNative(database="feast_test", collection=dest), + allow_overwrite=True, + ) + + client = MongoClient(mongodb_connection_string) + try: + docs = list(client["feast_test"][dest].find({}, {"_id": 0})) + finally: + client.close() + + assert len(docs) == 1 + assert "stale" not in docs[0] + assert "driver_id" in docs[0] From 84ac27b61809a132591f22291adf434ba1af55a5 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 17 Apr 2026 18:27:01 -0400 Subject: [PATCH 48/76] Add cross-implementation equivalence suite (test_cross.py) Seeds the same logical data in both storage schemas (single-collection for one/native, per-FV-collection for many) and asserts all three implementations return identical feature values for six scenarios: PIT join, TTL filtering, multi-FV join, overlapping feature names (full_feature_names=True), compound join keys, and extra label columns in entity_df. Also adds overlapping-feature-name coverage to the 'many' implementation, which was missing this case in its individual test suite. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .../mongodb_offline_store/test_cross.py | 796 ++++++++++++++++++ 1 file changed, 796 insertions(+) create mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_cross.py diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_cross.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_cross.py new file mode 100644 index 00000000000..e42b03beb10 --- /dev/null +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_cross.py @@ -0,0 +1,796 @@ +"""Cross-implementation equivalence suite for MongoDB offline stores. + +Each test seeds the same logical data in **both** storage schemas, runs all +three implementations, and asserts that the feature values returned by each +are identical for the same entity/timestamp inputs. + +Storage schemas +--------------- +single-collection (serves ``one`` and ``native``): + feature_history { entity_id: bytes, feature_view: str, features: {...}, + event_timestamp: datetime, created_at: datetime } + +per-FV-collection (serves ``many``): + { : val, ..., : val, ..., + event_timestamp: datetime } + +One call to ``_seed()`` writes both schemas so all three implementations read +from their natural format without any manual duplication in test bodies. +""" + +from __future__ import annotations + +from collections import defaultdict +from dataclasses import dataclass +from datetime import datetime, timedelta +from typing import Any, Callable, Dict, List, Optional +from unittest.mock import MagicMock + +import pandas as pd +import pytest +import pytz + +pytest.importorskip("pymongo") + +from pymongo import MongoClient +from testcontainers.mongodb import MongoDbContainer + +from feast import Entity, FeatureView, Field +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( + MongoDBOfflineStoreMany, + MongoDBOfflineStoreManyConfig, + MongoDBSourceMany, +) +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( + MongoDBOfflineStoreNative, + MongoDBOfflineStoreNativeConfig, + MongoDBSourceNative, +) +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( + MongoDBOfflineStoreOne, + MongoDBOfflineStoreOneConfig, + MongoDBSourceOne, +) +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import RepoConfig +from feast.types import Float64, Int64, String +from feast.value_type import ValueType + +# --------------------------------------------------------------------------- +# Docker guard +# --------------------------------------------------------------------------- + +docker_available = False +try: + import docker + + try: + _docker_client = docker.from_env() + _docker_client.ping() + docker_available = True + except Exception: + pass +except ImportError: + pass + +_requires_docker = pytest.mark.skipif( + not docker_available, + reason="Docker is not available or not running.", +) + +ENTITY_KEY_VERSION = 3 +# Separate DB so this suite never contaminates per-impl test suites +DB = "feast_cross" + + +# --------------------------------------------------------------------------- +# Entity key helper +# --------------------------------------------------------------------------- + + +def _make_entity_id( + join_keys: dict, + value_types: Optional[Dict[str, ValueType]] = None, +) -> bytes: + entity_key = EntityKeyProto() + for key in sorted(join_keys.keys()): + entity_key.join_keys.append(key) + val = ValueProto() + value = join_keys[key] + declared_type = value_types.get(key) if value_types else None + if declared_type == ValueType.INT32: + val.int32_val = int(value) + elif declared_type == ValueType.INT64: + val.int64_val = int(value) + elif declared_type == ValueType.STRING: + val.string_val = str(value) + else: + if isinstance(value, int): + val.int64_val = value + elif isinstance(value, str): + val.string_val = value + else: + val.string_val = str(value) + entity_key.entity_values.append(val) + return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) + + +# --------------------------------------------------------------------------- +# Store implementation descriptors +# --------------------------------------------------------------------------- + + +@dataclass +class StoreImpl: + """Bundles all implementation-specific factories into one object.""" + + id: str + store_class: Any + make_offline_config: Callable[[str], Any] # (conn_str) → offline store config + make_source: Callable[[str], Any] # (fv_name) → data source + + +ALL_IMPLS: List[StoreImpl] = [ + StoreImpl( + id="one", + store_class=MongoDBOfflineStoreOne, + make_offline_config=lambda conn: MongoDBOfflineStoreOneConfig( + connection_string=conn, + database=DB, + collection="feature_history", + ), + make_source=lambda fv_name: MongoDBSourceOne( + name=fv_name, + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + ), + ), + StoreImpl( + id="native", + store_class=MongoDBOfflineStoreNative, + make_offline_config=lambda conn: MongoDBOfflineStoreNativeConfig( + connection_string=conn, + database=DB, + collection="feature_history", + ), + make_source=lambda fv_name: MongoDBSourceNative( + name=fv_name, + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + ), + ), + StoreImpl( + id="many", + store_class=MongoDBOfflineStoreMany, + make_offline_config=lambda conn: MongoDBOfflineStoreManyConfig( + connection_string=conn, + database=DB, + ), + make_source=lambda fv_name: MongoDBSourceMany( + name=fv_name, + database=DB, + collection=fv_name, + timestamp_field="event_timestamp", + ), + ), +] + + +# --------------------------------------------------------------------------- +# Seed helper — writes both schemas from the same logical rows +# --------------------------------------------------------------------------- + + +def _seed(client: MongoClient, rows: List[Dict]) -> None: + """Insert rows into both storage schemas. + + Each element of *rows* is a dict with keys: + fv_name – feature view name (also the collection name for 'many') + entity_dict – {join_key: value, ...} + feature_dict – {feature_name: value, ...} + event_ts – datetime + + After this call: + - 'one' and 'native' can read from ``DB.feature_history`` + - 'many' can read from ``DB.`` + """ + db = client[DB] + + # ── Single-collection schema (one + native) ──────────────────────────── + db["feature_history"].insert_many( + [ + { + "entity_id": _make_entity_id(r["entity_dict"]), + "feature_view": r["fv_name"], + "features": r["feature_dict"], + "event_timestamp": r["event_ts"], + "created_at": r["event_ts"], + } + for r in rows + ] + ) + + # ── Per-FV-collection schema (many) ──────────────────────────────────── + by_fv: Dict[str, List[Dict]] = defaultdict(list) + for r in rows: + by_fv[r["fv_name"]].append(r) + + for fv_name, fv_rows in by_fv.items(): + db[fv_name].insert_many( + [ + { + **r["entity_dict"], + **r["feature_dict"], + "event_timestamp": r["event_ts"], + } + for r in fv_rows + ] + ) + + +# --------------------------------------------------------------------------- +# Run helper +# --------------------------------------------------------------------------- + + +def _run( + impl: StoreImpl, + conn_str: str, + fvs: List[FeatureView], + feature_refs: List[str], + entity_df: pd.DataFrame, + full_feature_names: bool = False, +) -> pd.DataFrame: + """Run get_historical_features for one implementation and return the result.""" + config = RepoConfig( + project="cross_test", + registry="memory://", + provider="local", + offline_store=impl.make_offline_config(conn_str), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + job = impl.store_class.get_historical_features( + config=config, + feature_views=fvs, + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="cross_test", + full_feature_names=full_feature_names, + ) + return job.to_df() + + +# --------------------------------------------------------------------------- +# Assertion helper +# --------------------------------------------------------------------------- + + +def _assert_equivalence( + dfs: List[pd.DataFrame], + ids: List[str], + sort_cols: List[str], + feature_cols: List[str], +) -> None: + """Assert all DataFrames return identical feature values after sorting. + + Lenient about column order, extra columns (e.g. entity key columns that + differ between schemas), and numeric dtype coercion. NaN == NaN for + the purposes of this comparison. + """ + normed = [df.sort_values(sort_cols).reset_index(drop=True) for df in dfs] + ref, ref_id = normed[0], ids[0] + + for other, other_id in zip(normed[1:], ids[1:]): + for col in feature_cols: + pd.testing.assert_series_equal( + ref[col].reset_index(drop=True), + other[col].reset_index(drop=True), + check_names=False, + check_dtype=False, + rtol=1e-5, + obj=f"column '{col}' ({ref_id} vs {other_id})", + ) + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +@pytest.fixture(scope="module") +def mongodb_container(): + container = MongoDbContainer( + "mongo:latest", + username="test", + password="test", # pragma: allowlist secret + ).with_exposed_ports(27017) + container.start() + yield container + container.stop() + + +@pytest.fixture +def conn_str(mongodb_container) -> str: + port = mongodb_container.get_exposed_port(27017) + return f"mongodb://test:test@localhost:{port}" # pragma: allowlist secret + + +# --------------------------------------------------------------------------- +# Tests +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_pit_join_equivalence(conn_str: str) -> None: + """PIT join returns the same feature values across all three implementations. + + Uses a time-varying dataset (three rows for driver 1 at different timestamps) + and three entity rows that each point to a different historical slice. + """ + now = datetime.now(tz=pytz.UTC) + fv = "driver_pit_x" + + client = MongoClient(conn_str) + _seed( + client, + [ + { + "fv_name": fv, + "entity_dict": {"driver_id": 1}, + "feature_dict": {"conv_rate": 0.5, "acc_rate": 0.9}, + "event_ts": now - timedelta(hours=2), + }, + { + "fv_name": fv, + "entity_dict": {"driver_id": 1}, + "feature_dict": {"conv_rate": 0.6, "acc_rate": 0.85}, + "event_ts": now - timedelta(hours=1), + }, + { + "fv_name": fv, + "entity_dict": {"driver_id": 1}, + "feature_dict": {"conv_rate": 0.7, "acc_rate": 0.8}, + "event_ts": now, + }, + { + "fv_name": fv, + "entity_dict": {"driver_id": 2}, + "feature_dict": {"conv_rate": 0.3, "acc_rate": 0.95}, + "event_ts": now - timedelta(hours=2), + }, + ], + ) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + entity_df = pd.DataFrame( + { + "driver_id": [1, 1, 2], + "event_timestamp": [ + now - timedelta(hours=1, minutes=30), # → 0.5 + now - timedelta(minutes=30), # → 0.6 + now - timedelta(hours=1), # → 0.3 + ], + } + ) + + dfs = [] + for impl in ALL_IMPLS: + source = impl.make_source(fv) + feature_view = FeatureView( + name=fv, + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + Field(name="acc_rate", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + dfs.append( + _run( + impl, + conn_str, + [feature_view], + [f"{fv}:conv_rate", f"{fv}:acc_rate"], + entity_df, + ) + ) + + _assert_equivalence( + dfs, + [impl.id for impl in ALL_IMPLS], + sort_cols=["driver_id", "event_timestamp"], + feature_cols=["conv_rate", "acc_rate"], + ) + + +@_requires_docker +def test_ttl_equivalence(conn_str: str) -> None: + """All three implementations produce NULL for stale features and a value for fresh ones. + + Tests that TTL filtering is consistent: driver 1 (within 1-day TTL) has a + non-NULL conv_rate; driver 2 (2 days old, outside TTL) has a NULL conv_rate. + """ + now = datetime.now(tz=pytz.UTC) + fv = "driver_ttl_x" + + client = MongoClient(conn_str) + _seed( + client, + [ + { + "fv_name": fv, + "entity_dict": {"driver_id": 1}, + "feature_dict": {"conv_rate": 0.9}, + "event_ts": now - timedelta(hours=1), + }, + { + "fv_name": fv, + "entity_dict": {"driver_id": 2}, + "feature_dict": {"conv_rate": 0.5}, + "event_ts": now - timedelta(days=2), # stale + }, + ], + ) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + + dfs = [] + for impl in ALL_IMPLS: + source = impl.make_source(fv) + feature_view = FeatureView( + name=fv, + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + dfs.append(_run(impl, conn_str, [feature_view], [f"{fv}:conv_rate"], entity_df)) + + # Feature values must be identical across implementations + _assert_equivalence( + dfs, + [impl.id for impl in ALL_IMPLS], + sort_cols=["driver_id"], + feature_cols=["conv_rate"], + ) + + # Verify the NULL pattern itself is consistent + for df, impl in zip(dfs, ALL_IMPLS): + normed = df.sort_values("driver_id").reset_index(drop=True) + assert not pd.isna(normed.loc[0, "conv_rate"]), ( + f"{impl.id}: driver 1 should be non-NULL (within TTL)" + ) + assert pd.isna(normed.loc[1, "conv_rate"]), ( + f"{impl.id}: driver 2 should be NULL (outside TTL)" + ) + + +@_requires_docker +def test_multi_fv_equivalence(conn_str: str) -> None: + """Features from two FVs joined on the same entity key are identical across all impls.""" + now = datetime.now(tz=pytz.UTC) + fv_driver = "driver_mfv_x" + fv_vehicle = "vehicle_mfv_x" + + client = MongoClient(conn_str) + _seed( + client, + [ + { + "fv_name": fv_driver, + "entity_dict": {"driver_id": 1}, + "feature_dict": {"rating": 4.8}, + "event_ts": now - timedelta(hours=1), + }, + { + "fv_name": fv_driver, + "entity_dict": {"driver_id": 2}, + "feature_dict": {"rating": 4.5}, + "event_ts": now - timedelta(hours=1), + }, + { + "fv_name": fv_vehicle, + "entity_dict": {"driver_id": 1}, + "feature_dict": {"vehicle_age": 2, "mileage": 50000}, + "event_ts": now - timedelta(hours=1), + }, + { + "fv_name": fv_vehicle, + "entity_dict": {"driver_id": 2}, + "feature_dict": {"vehicle_age": 5, "mileage": 120000}, + "event_ts": now - timedelta(hours=1), + }, + ], + ) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + + dfs = [] + for impl in ALL_IMPLS: + fv1 = FeatureView( + name=fv_driver, + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="rating", dtype=Float64), + ], + source=impl.make_source(fv_driver), + ttl=timedelta(days=1), + ) + fv2 = FeatureView( + name=fv_vehicle, + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="vehicle_age", dtype=Int64), + Field(name="mileage", dtype=Int64), + ], + source=impl.make_source(fv_vehicle), + ttl=timedelta(days=1), + ) + dfs.append( + _run( + impl, + conn_str, + [fv1, fv2], + [ + f"{fv_driver}:rating", + f"{fv_vehicle}:vehicle_age", + f"{fv_vehicle}:mileage", + ], + entity_df, + ) + ) + + _assert_equivalence( + dfs, + [impl.id for impl in ALL_IMPLS], + sort_cols=["driver_id"], + feature_cols=["rating", "vehicle_age", "mileage"], + ) + + +@_requires_docker +def test_overlapping_feature_names_equivalence(conn_str: str) -> None: + """Three FVs sharing a feature named 'score' are handled identically with full_feature_names=True. + + This also adds overlapping-feature-name coverage to the 'many' implementation, + which lacked this test in its individual suite. + """ + now = datetime.now(tz=pytz.UTC) + fv_names = ["fv_ol_xa", "fv_ol_xb", "fv_ol_xc"] + bases = {"fv_ol_xa": 1.0, "fv_ol_xb": 2.0, "fv_ol_xc": 3.0} + + client = MongoClient(conn_str) + rows = [] + for fv_name in fv_names: + for driver_id in [1, 2]: + rows.append( + { + "fv_name": fv_name, + "entity_dict": {"driver_id": driver_id}, + "feature_dict": {"score": bases[fv_name] + driver_id * 0.1}, + "event_ts": now - timedelta(hours=1), + } + ) + _seed(client, rows) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + + dfs = [] + for impl in ALL_IMPLS: + fvs = [ + FeatureView( + name=fv_name, + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="score", dtype=Float64), + ], + source=impl.make_source(fv_name), + ttl=timedelta(days=1), + ) + for fv_name in fv_names + ] + dfs.append( + _run( + impl, + conn_str, + fvs, + [f"{n}:score" for n in fv_names], + entity_df, + full_feature_names=True, + ) + ) + + prefixed_cols = [f"{n}__score" for n in fv_names] + _assert_equivalence( + dfs, + [impl.id for impl in ALL_IMPLS], + sort_cols=["driver_id"], + feature_cols=prefixed_cols, + ) + + +@_requires_docker +def test_compound_keys_equivalence(conn_str: str) -> None: + """Compound join keys (user_id, device_id) produce identical results across all impls.""" + now = datetime.now(tz=pytz.UTC) + fv = "user_device_x" + + client = MongoClient(conn_str) + _seed( + client, + [ + { + "fv_name": fv, + "entity_dict": {"user_id": 1, "device_id": "mobile"}, + "feature_dict": {"app_opens": 55}, + "event_ts": now - timedelta(hours=1), + }, + { + "fv_name": fv, + "entity_dict": {"user_id": 1, "device_id": "desktop"}, + "feature_dict": {"app_opens": 10}, + "event_ts": now - timedelta(hours=1), + }, + { + "fv_name": fv, + "entity_dict": {"user_id": 2, "device_id": "tablet"}, + "feature_dict": {"app_opens": 25}, + "event_ts": now - timedelta(hours=1), + }, + ], + ) + client.close() + + user_entity = Entity( + name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 + ) + device_entity = Entity( + name="device_id", join_keys=["device_id"], value_type=ValueType.STRING + ) + entity_df = pd.DataFrame( + { + "user_id": [1, 1, 2], + "device_id": ["mobile", "desktop", "tablet"], + "event_timestamp": [now, now, now], + } + ) + + dfs = [] + for impl in ALL_IMPLS: + feature_view = FeatureView( + name=fv, + entities=[user_entity, device_entity], + schema=[ + Field(name="user_id", dtype=Int64), + Field(name="device_id", dtype=String), + Field(name="app_opens", dtype=Int64), + ], + source=impl.make_source(fv), + ttl=timedelta(days=1), + ) + dfs.append(_run(impl, conn_str, [feature_view], [f"{fv}:app_opens"], entity_df)) + + _assert_equivalence( + dfs, + [impl.id for impl in ALL_IMPLS], + sort_cols=["user_id", "device_id"], + feature_cols=["app_opens"], + ) + + +@_requires_docker +def test_extra_columns_equivalence(conn_str: str) -> None: + """Extra label columns in entity_df pass through unchanged and do not affect features. + + This is the cross-implementation regression test for the union-key bug: if any + implementation folds non-join-key columns into the entity key, features come + back NULL. Here we verify that all three return the same non-NULL feature + values and preserve the label column. + """ + now = datetime.now(tz=pytz.UTC) + fv = "driver_extra_x" + + client = MongoClient(conn_str) + _seed( + client, + [ + { + "fv_name": fv, + "entity_dict": {"driver_id": 1}, + "feature_dict": {"conv_rate": 0.5}, + "event_ts": now - timedelta(hours=2), + }, + { + "fv_name": fv, + "entity_dict": {"driver_id": 1}, + "feature_dict": {"conv_rate": 0.6}, + "event_ts": now - timedelta(hours=1), + }, + { + "fv_name": fv, + "entity_dict": {"driver_id": 2}, + "feature_dict": {"conv_rate": 0.3}, + "event_ts": now - timedelta(hours=2), + }, + ], + ) + client.close() + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + entity_df = pd.DataFrame( + { + "driver_id": [1, 1, 2], + "event_timestamp": [ + now - timedelta(hours=1, minutes=30), + now - timedelta(minutes=30), + now - timedelta(hours=1), + ], + "trip_success": [1, 0, 1], # label column — must not enter entity key + } + ) + + dfs = [] + for impl in ALL_IMPLS: + source = impl.make_source(fv) + feature_view = FeatureView( + name=fv, + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + dfs.append(_run(impl, conn_str, [feature_view], [f"{fv}:conv_rate"], entity_df)) + + # Feature values must be identical + _assert_equivalence( + dfs, + [impl.id for impl in ALL_IMPLS], + sort_cols=["driver_id", "event_timestamp"], + feature_cols=["conv_rate"], + ) + + # Features must be non-NULL (union-key bug would produce all-NULL here) + for df, impl in zip(dfs, ALL_IMPLS): + assert df["conv_rate"].notna().all(), ( + f"{impl.id}: conv_rate is null — label column may have been " + "folded into the entity key serialization." + ) + + # Label column must be present and unchanged in all three + for df, impl in zip(dfs, ALL_IMPLS): + assert "trip_success" in df.columns, f"{impl.id}: trip_success column missing" + assert sorted(df["trip_success"].tolist()) == [0, 1, 1], ( + f"{impl.id}: trip_success values changed" + ) From 8346f284bc00927f5f59569d59bc2f6f44f639a4 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 17 Apr 2026 19:41:19 -0400 Subject: [PATCH 49/76] Add benchmark_sweep.py: four-dimensional scaling suite across all three implementations Covers N (entity count), M (feature width), P (observation depth), K (feature view fan-out) as independent dimensions with smoke/stress/OOM-guard tiers. Side-by-side table printed after each parametrize run; results appended to benchmark_results.csv. Replaces the one/many-only benchmark.py. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .../mongodb_offline_store/benchmark_sweep.py | 953 ++++++++++++++++++ 1 file changed, 953 insertions(+) create mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py new file mode 100644 index 00000000000..0259ce4e0aa --- /dev/null +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py @@ -0,0 +1,953 @@ +"""MongoDB offline store benchmark suite — v2. + +Sweeps four independent dimensions across all three implementations +side-by-side in every parametrize run: + + N entity count (entity_df rows) + M feature width (features per feature view) + P observation depth (historical rows per entity in the collection) + K feature view fan-out (feature views joined simultaneously) + +Implementations +--------------- + one — single collection, Python-side PIT join (pandas merge_asof) + native — single collection, Atlas-side PIT join ($documents + $lookup) + many — per-FV collections, Ibis-based PIT join + +P is the key differentiator: native's $lookup subpipeline uses +``{$sort: {event_timestamp: -1}, $limit: 1}`` backed by the compound index, +so its cost is O(log P) per lookup. one and many must transfer all N×P +documents from MongoDB and deduplicate in Python — cost is O(N×P). + +Output +------ + stdout side-by-side table after each parametrize run + CSV benchmark_results.csv in this directory (appended, never overwritten) + +Usage +----- + Smoke (default, ~2–3 min): + pytest benchmark_v2.py -v -s + + Skip slow stress tests: + pytest benchmark_v2.py -v -s -m "not slow" + + Stress only: + pytest benchmark_v2.py -v -s -m slow + + Full sweep — edit the *_FULL constants below and swap them into the + @pytest.mark.parametrize decorators: + pytest benchmark_v2.py -v -s +""" + +from __future__ import annotations + +import csv +import gc +import resource +import sys +import time +import tracemalloc +from dataclasses import dataclass, field +from datetime import datetime, timedelta +from pathlib import Path +from typing import Any, Callable, Dict, Generator, List, Optional + +import pandas as pd +import pytest +import pytz + +pytest.importorskip("pymongo") + +from unittest.mock import MagicMock + +from pymongo import MongoClient +from testcontainers.mongodb import MongoDbContainer + +from feast import Entity, FeatureView, Field +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( + MongoDBOfflineStoreMany, + MongoDBOfflineStoreManyConfig, + MongoDBSourceMany, +) +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( + MongoDBOfflineStoreNative, + MongoDBOfflineStoreNativeConfig, + MongoDBSourceNative, +) +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( + MongoDBOfflineStoreOne, + MongoDBOfflineStoreOneConfig, + MongoDBSourceOne, +) +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import RepoConfig +from feast.types import Float64, Int64 +from feast.value_type import ValueType + +# --------------------------------------------------------------------------- +# Docker guard +# --------------------------------------------------------------------------- + +docker_available = False +try: + import docker + + try: + _docker_client = docker.from_env() + _docker_client.ping() + docker_available = True + except Exception: + pass +except ImportError: + pass + +_requires_docker = pytest.mark.skipif( + not docker_available, + reason="Docker is not available or not running.", +) + +# --------------------------------------------------------------------------- +# Constants +# --------------------------------------------------------------------------- + +ENTITY_KEY_VERSION = 3 +BENCH_DB = "bench_db" +BENCH_FH = "bench_fh" # single-collection name shared by 'one' and 'native' +_INSERT_BATCH = 50_000 # docs per insert_many call + +_CSV_PATH = Path(__file__).with_name("benchmark_results.csv") + +# --------------------------------------------------------------------------- +# Sweep ranges +# --------------------------------------------------------------------------- +# +# Smoke tier (default) — moderate sizes, safe for the Docker testcontainer. +# Each sweep holds the other three dimensions at their baseline. + +N_BASE, M_BASE, P_BASE, K_BASE = 500, 10, 5, 1 + +N_SMOKE = [200, 1_000, 4_000] # entities in entity_df +M_SMOKE = [5, 20, 50] # features per feature view +P_SMOKE = [1, 5, 20] # historical rows per entity +K_SMOKE = [1, 3] # feature views joined + +# 2×2 N×P interaction grid — reveals P-independence of 'native' +N_P_GRID_SMOKE = [(500, 5), (500, 20), (2_000, 5), (2_000, 20)] + +# Full tier — swap into the @pytest.mark.parametrize decorators for local runs. +# N_FULL = [200, 1_000, 5_000, 20_000, 100_000] +# M_FULL = [5, 20, 50, 100, 200] +# P_FULL = [1, 5, 20, 60, 120] +# K_FULL = [1, 3, 7, 15] +# N_P_GRID_FULL = [(1_000,5),(1_000,60),(10_000,5),(10_000,60)] + +# Stress tier — @pytest.mark.slow, large N. +N_STRESS = [50_000, 200_000] +M_STRESS, P_STRESS = 50, 5 + +# --------------------------------------------------------------------------- +# Entity key helper (int entity_id → serialized bytes) +# --------------------------------------------------------------------------- + + +def _make_entity_id(entity_id: int) -> bytes: + entity_key = EntityKeyProto() + entity_key.join_keys.append("driver_id") + val = ValueProto() + val.int64_val = entity_id + entity_key.entity_values.append(val) + return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) + + +# --------------------------------------------------------------------------- +# Store implementation descriptors +# --------------------------------------------------------------------------- + + +@dataclass +class StoreImpl: + """Bundles all implementation-specific factories into one object.""" + + id: str + store_class: Any + make_offline_config: Callable[[str], Any] # (conn_str) → offline store config + make_source: Callable[[str], Any] # (fv_name) → data source + + +ALL_IMPLS: List[StoreImpl] = [ + StoreImpl( + id="one", + store_class=MongoDBOfflineStoreOne, + make_offline_config=lambda conn: MongoDBOfflineStoreOneConfig( + connection_string=conn, + database=BENCH_DB, + collection=BENCH_FH, + ), + make_source=lambda fv_name: MongoDBSourceOne( + name=fv_name, + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + ), + ), + StoreImpl( + id="native", + store_class=MongoDBOfflineStoreNative, + make_offline_config=lambda conn: MongoDBOfflineStoreNativeConfig( + connection_string=conn, + database=BENCH_DB, + collection=BENCH_FH, + ), + make_source=lambda fv_name: MongoDBSourceNative( + name=fv_name, + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + ), + ), + StoreImpl( + id="many", + store_class=MongoDBOfflineStoreMany, + make_offline_config=lambda conn: MongoDBOfflineStoreManyConfig( + connection_string=conn, + database=BENCH_DB, + ), + make_source=lambda fv_name: MongoDBSourceMany( + name=fv_name, + database=BENCH_DB, + collection=fv_name, + timestamp_field="event_timestamp", + ), + ), +] + +# --------------------------------------------------------------------------- +# Seed helper — writes both schemas simultaneously +# --------------------------------------------------------------------------- + + +def _seed_benchmark( + client: MongoClient, + fv_names: List[str], + num_entities: int, + num_features: int, + rows_per_entity: int, +) -> datetime: + """Insert data in both the single-collection and per-FV schemas. + + Single-collection (BENCH_FH) serves 'one' and 'native'. + Per-FV collections (one per fv_name) serve 'many'. + + Returns the ``now`` timestamp (the most recent event_timestamp seeded). + """ + import numpy as np + + db = client[BENCH_DB] + now = datetime.now(tz=pytz.UTC) + # P timestamps: now, now-1h, now-2h, …, now-(P-1)h + timestamps = [now - timedelta(hours=p) for p in range(rows_per_entity)] + feat_names = [f"feature_{f}" for f in range(num_features)] + + for fv_idx, fv_name in enumerate(fv_names): + rng = np.random.default_rng(seed=fv_idx) + feat_matrix = rng.random((num_entities * rows_per_entity, num_features)).astype( + float + ) + + single_batch: List[Dict] = [] + many_batch: List[Dict] = [] + row_idx = 0 + + for entity_id in range(num_entities): + eid_bytes = _make_entity_id(entity_id) + for p in range(rows_per_entity): + ts = timestamps[p] + feat_vals = { + feat_names[f]: float(feat_matrix[row_idx, f]) + for f in range(num_features) + } + row_idx += 1 + + # Single-collection schema (one + native) + single_batch.append( + { + "entity_id": eid_bytes, + "feature_view": fv_name, + "features": feat_vals, + "event_timestamp": ts, + "created_at": ts, + } + ) + # Per-FV schema (many) + many_batch.append( + {"driver_id": entity_id, **feat_vals, "event_timestamp": ts} + ) + + if len(single_batch) >= _INSERT_BATCH: + db[BENCH_FH].insert_many(single_batch) + db[fv_name].insert_many(many_batch) + single_batch = [] + many_batch = [] + + if single_batch: + db[BENCH_FH].insert_many(single_batch) + db[fv_name].insert_many(many_batch) + + return now + + +def _reset_and_seed( + client: MongoClient, + fv_names: List[str], + num_entities: int, + num_features: int, + rows_per_entity: int, +) -> datetime: + """Drop benchmark collections, then seed fresh data for this run.""" + db = client[BENCH_DB] + db[BENCH_FH].drop() + for fv_name in fv_names: + db[fv_name].drop() + return _seed_benchmark( + client, fv_names, num_entities, num_features, rows_per_entity + ) + + +# --------------------------------------------------------------------------- +# Measurement harness +# --------------------------------------------------------------------------- + + +def _rss_mb() -> float: + """Current process peak RSS in MB (lifetime high-water-mark from the OS). + + We snapshot before/after each operation; the delta reflects the high-water + added by that operation. On macOS ru_maxrss is in bytes; on Linux, KB. + """ + rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss + return rss / (1024 * 1024) if sys.platform == "darwin" else rss / 1024 + + +@dataclass +class BenchResult: + elapsed_s: float + trace_mb: float # tracemalloc Python-heap peak for this operation + rss_mb: float = 0.0 # RSS growth attributed to this operation + mongo_ops: Dict[str, int] = field(default_factory=dict) + status: str = "OK" # "OK" | "OOM" | "SKIPPED:…" | "ERROR:…" + + +def _run_measured(func: Callable, mongo_client: Optional[Any] = None) -> BenchResult: + """Run func() and capture elapsed time, tracemalloc peak, RSS delta, Mongo ops.""" + # MongoDB opcounters snapshot + mongo_before: Optional[Dict] = None + if mongo_client: + try: + status = mongo_client.admin.command("serverStatus") + mongo_before = dict(status.get("opcounters", {})) + except Exception: + pass + + rss_before = _rss_mb() + tracemalloc.start() + t0 = time.perf_counter() + + outcome = "OK" + try: + func() + except MemoryError: + outcome = "OOM" + except Exception as exc: + outcome = f"ERROR:{str(exc)[:80]}" + + elapsed = time.perf_counter() - t0 + _, peak_bytes = tracemalloc.get_traced_memory() + tracemalloc.stop() + rss_after = _rss_mb() + + mongo_delta: Dict[str, int] = {} + if mongo_client and mongo_before: + try: + after = mongo_client.admin.command("serverStatus") + after_ops = dict(after.get("opcounters", {})) + mongo_delta = { + k: after_ops.get(k, 0) - mongo_before.get(k, 0) for k in after_ops + } + except Exception: + pass + + return BenchResult( + elapsed_s=elapsed, + trace_mb=peak_bytes / (1024 * 1024), + rss_mb=max(rss_after - rss_before, 0.0), + mongo_ops=mongo_delta, + status=outcome, + ) + + +# --------------------------------------------------------------------------- +# OOM projection utilities (carried over from benchmark.py) +# --------------------------------------------------------------------------- + +# Empirical overhead: bytes consumed by 'many' per raw float value. +# Measured in stress runs: tracemalloc peak / (N × P × M floats). +_MANY_BYTES_PER_FLOAT = 130 + + +def _projected_many_mb(N: int, P: int, M: int) -> float: + return N * P * M * _MANY_BYTES_PER_FLOAT / 1e6 + + +def _free_memory_mb() -> float: + """Estimate currently available RAM in MB (macOS vm_stat or /proc/meminfo).""" + try: + import subprocess + + out = subprocess.check_output(["vm_stat"], text=True) + page_size = 16_384 + try: + hw = subprocess.check_output( + ["sysctl", "-n", "hw.pagesize"], text=True + ).strip() + page_size = int(hw) + except Exception: + pass + free = purgeable = 0 + for line in out.splitlines(): + if line.startswith("Pages free:"): + free = int(line.split(":")[1].strip().rstrip(".")) + elif line.startswith("Pages purgeable:"): + purgeable = int(line.split(":")[1].strip().rstrip(".")) + return (free + purgeable) * page_size / (1024 * 1024) + except Exception: + pass + try: + with open("/proc/meminfo") as f: + for line in f: + if line.startswith("MemAvailable:"): + return int(line.split()[1]) / 1024 + except Exception: + pass + return 4_096 # conservative fallback + + +def _many_safe(N: int, P: int, M: int, safety: float = 0.80) -> tuple: + """Return (safe, projected_mb, free_mb).""" + proj = _projected_many_mb(N, P, M) + free = _free_memory_mb() + return proj < free * safety, proj, free + + +# --------------------------------------------------------------------------- +# FV / config factories +# --------------------------------------------------------------------------- + + +def _make_fv(impl: StoreImpl, fv_name: str, num_features: int) -> FeatureView: + entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + schema = [Field(name="driver_id", dtype=Int64)] + [ + Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) + ] + return FeatureView( + name=fv_name, + entities=[entity], + schema=schema, + source=impl.make_source(fv_name), + ttl=timedelta(days=100), # generous — never filters during benchmark + ) + + +def _make_config(impl: StoreImpl, conn_str: str) -> RepoConfig: + return RepoConfig( + project="benchmark", + registry="memory://", + provider="local", + offline_store=impl.make_offline_config(conn_str), + online_store={"type": "sqlite"}, + entity_key_serialization_version=ENTITY_KEY_VERSION, + ) + + +# --------------------------------------------------------------------------- +# Run all three implementations for one scenario +# --------------------------------------------------------------------------- + + +def _run_all( + conn_str: str, + fv_names: List[str], + num_features: int, + entity_df: pd.DataFrame, + mongo_client: Any, + skip_many_if_oom: bool = False, + N: int = 0, + P: int = 0, +) -> Dict[str, BenchResult]: + """Run get_historical_features on every impl and return per-impl BenchResult.""" + feature_refs = [f"{n}:feature_{f}" for n in fv_names for f in range(num_features)] + results: Dict[str, BenchResult] = {} + + for impl in ALL_IMPLS: + # OOM guard for 'many' in stress scenarios + if skip_many_if_oom and impl.id == "many" and N > 0 and P > 0: + safe, proj_mb, free_mb = _many_safe(N, P, num_features) + if not safe: + results[impl.id] = BenchResult( + elapsed_s=0, + trace_mb=proj_mb, + status=f"SKIPPED:{proj_mb:.0f}MB_projected", + ) + print( + f" [many] SKIPPED — projected {proj_mb:.0f} MB " + f"> 80 % of {free_mb:.0f} MB free" + ) + continue + + gc.collect() # release residual memory from previous impl + + config = _make_config(impl, conn_str) + fvs = [_make_fv(impl, n, num_features) for n in fv_names] + + def _query(impl=impl, config=config, fvs=fvs): + job = impl.store_class.get_historical_features( + config=config, + feature_views=fvs, + feature_refs=feature_refs, + entity_df=entity_df, + registry=MagicMock(), + project="benchmark", + full_feature_names=True, + ) + return job.to_df() + + results[impl.id] = _run_measured(_query, mongo_client=mongo_client) + + return results + + +# --------------------------------------------------------------------------- +# Output helpers +# --------------------------------------------------------------------------- + +_COL_W = 13 # column width per implementation in the table + + +def _print_table(test: str, params: Dict, results: Dict[str, BenchResult]) -> None: + """Print a side-by-side results table to stdout.""" + impl_ids = [impl.id for impl in ALL_IMPLS] + N = params.get("N", 0) + + title = ( + f"[{test}] N={params.get('N', '?')} " + f"M={params.get('M', '?')} " + f"P={params.get('P', '?')} " + f"K={params.get('K', '?')}" + ) + bar = "─" * (22 + _COL_W * len(impl_ids)) + print(f"\n{title}") + print(f" {'':22}" + "".join(f"{i:>{_COL_W}}" for i in impl_ids)) + print(f" {bar}") + + def _cell(r: Optional[BenchResult], metric: str) -> str: + if r is None: + return "—" + if r.status != "OK" and metric != "status": + return r.status[: _COL_W - 1] + if metric == "time_s": + return f"{r.elapsed_s:.3f}" + if metric == "trace_mb": + return f"{r.trace_mb:.1f}" + if metric == "rss_mb": + return f"{r.rss_mb:.1f}" + if metric == "rows_s": + return f"{N / r.elapsed_s:,.0f}" if r.elapsed_s > 0 and N > 0 else "—" + if metric == "status": + return r.status + return "—" + + for metric, label in [ + ("time_s", "time (s)"), + ("trace_mb", "trace MB"), + ("rss_mb", "RSS Δ MB"), + ("rows_s", "rows/s"), + ("status", "status"), + ]: + row = f" {label:22}" + "".join( + f"{_cell(results.get(iid), metric):>{_COL_W}}" for iid in impl_ids + ) + print(row) + + +def _append_csv(test: str, params: Dict, results: Dict[str, BenchResult]) -> None: + """Append one row per implementation to benchmark_results.csv.""" + from datetime import datetime as _dt + + ts = _dt.now().isoformat(timespec="seconds") + N = params.get("N", 0) + fieldnames = [ + "timestamp", + "test", + "impl", + "N", + "M", + "P", + "K", + "elapsed_s", + "trace_mb", + "rss_mb", + "rows_per_s", + "status", + ] + rows = [ + { + "timestamp": ts, + "test": test, + "impl": impl_id, + "N": params.get("N", ""), + "M": params.get("M", ""), + "P": params.get("P", ""), + "K": params.get("K", ""), + "elapsed_s": round(r.elapsed_s, 4), + "trace_mb": round(r.trace_mb, 2), + "rss_mb": round(r.rss_mb, 2), + "rows_per_s": round(N / r.elapsed_s, 1) if r.elapsed_s > 0 and N > 0 else 0, + "status": r.status, + } + for impl_id, r in results.items() + ] + write_header = not _CSV_PATH.exists() + with _CSV_PATH.open("a", newline="") as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + if write_header: + writer.writeheader() + writer.writerows(rows) + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +@pytest.fixture(scope="module") +def mongodb_container() -> Generator[MongoDbContainer, None, None]: + container = MongoDbContainer( + "mongo:latest", + username="test", + password="test", # pragma: allowlist secret + ).with_exposed_ports(27017) + container.start() + yield container + container.stop() + + +@pytest.fixture +def conn_str(mongodb_container: MongoDbContainer) -> str: + port = mongodb_container.get_exposed_port(27017) + return f"mongodb://test:test@localhost:{port}" # pragma: allowlist secret + + +# ============================================================================= +# Sweep 1: N — entity count +# +# What to look for: +# one, many : time and memory grow roughly linearly with N (they load N×P docs) +# native : time grows linearly with N (one $lookup per entity row), +# memory grows linearly with N (result is N×M features) +# but the per-entity cost should be lower than one/many because +# the PIT deduplication happens inside the $lookup on the server. +# ============================================================================= + + +@_requires_docker +@pytest.mark.parametrize("N", N_SMOKE) +def test_scale_N(conn_str: str, N: int) -> None: + """Sweep N (entities). Hold M=M_BASE, P=P_BASE, K=K_BASE.""" + M, P, K = M_BASE, P_BASE, K_BASE + fv_names = [f"fv_{k}" for k in range(K)] + params = {"N": N, "M": M, "P": P, "K": K} + + client = MongoClient(conn_str) + try: + now = _reset_and_seed(client, fv_names, N, M, P) + entity_df = pd.DataFrame( + {"driver_id": list(range(N)), "event_timestamp": [now] * N} + ) + results = _run_all(conn_str, fv_names, M, entity_df, client) + finally: + client.close() + + _print_table("scale_N", params, results) + _append_csv("scale_N", params, results) + + +# ============================================================================= +# Sweep 2: M — feature width +# +# What to look for: +# native : M affects result size only (one doc per entity row returned); +# the $lookup pipeline itself is M-independent +# one : M affects how many keys are extracted from each features subdoc +# (per-feature .apply()) — should scale gently with M +# many : M inflates every stored document; pandas DataFrame grows O(N×P×M) +# ============================================================================= + + +@_requires_docker +@pytest.mark.parametrize("M", M_SMOKE) +def test_scale_M(conn_str: str, M: int) -> None: + """Sweep M (features per FV). Hold N=N_BASE, P=P_BASE, K=K_BASE.""" + N, P, K = N_BASE, P_BASE, K_BASE + fv_names = [f"fv_{k}" for k in range(K)] + params = {"N": N, "M": M, "P": P, "K": K} + + client = MongoClient(conn_str) + try: + now = _reset_and_seed(client, fv_names, N, M, P) + entity_df = pd.DataFrame( + {"driver_id": list(range(N)), "event_timestamp": [now] * N} + ) + results = _run_all(conn_str, fv_names, M, entity_df, client) + finally: + client.close() + + _print_table("scale_M", params, results) + _append_csv("scale_M", params, results) + + +# ============================================================================= +# Sweep 3: P — observation depth (historical rows per entity) +# +# This is the most diagnostic sweep for the native implementation. +# +# What to look for: +# native : time and memory should be nearly flat as P grows — the $lookup +# subpipeline finds the single matching document via the compound +# index (entity_id, feature_view, event_timestamp DESC) and never +# materialises the other P-1 rows. Cost is O(log P) not O(P). +# one : fetches all N×P documents matching the entity_id $in list, then +# does pandas merge_asof. Both time and memory scale O(P). +# many : loads the entire collection (N×P docs) into pandas before joining. +# Both time and memory scale O(P). +# ============================================================================= + + +@_requires_docker +@pytest.mark.parametrize("P", P_SMOKE) +def test_scale_P(conn_str: str, P: int) -> None: + """Sweep P (observations per entity). Hold N=N_BASE, M=M_BASE, K=K_BASE.""" + N, M, K = N_BASE, M_BASE, K_BASE + fv_names = [f"fv_{k}" for k in range(K)] + params = {"N": N, "M": M, "P": P, "K": K} + + client = MongoClient(conn_str) + try: + now = _reset_and_seed(client, fv_names, N, M, P) + entity_df = pd.DataFrame( + {"driver_id": list(range(N)), "event_timestamp": [now] * N} + ) + results = _run_all(conn_str, fv_names, M, entity_df, client) + finally: + client.close() + + _print_table("scale_P", params, results) + _append_csv("scale_P", params, results) + + +# ============================================================================= +# Sweep 4: K — feature view fan-out +# +# What to look for: +# native : each additional FV adds one $lookup stage to the pipeline; +# the entire multi-FV join runs in a single aggregation round-trip. +# Overhead per FV should be small. +# one : one aggregation query per FV (chunked), then one merge_asof pass +# per FV. Time grows roughly linearly with K. +# many : one full collection scan per FV, then one merge_asof per FV. +# Both time and memory grow linearly with K. +# ============================================================================= + + +@_requires_docker +@pytest.mark.parametrize("K", K_SMOKE) +def test_scale_K(conn_str: str, K: int) -> None: + """Sweep K (feature views joined). Hold N=N_BASE, M=M_BASE, P=P_BASE.""" + N, M, P = N_BASE, M_BASE, P_BASE + fv_names = [f"fv_{k}" for k in range(K)] + params = {"N": N, "M": M, "P": P, "K": K} + + client = MongoClient(conn_str) + try: + now = _reset_and_seed(client, fv_names, N, M, P) + entity_df = pd.DataFrame( + {"driver_id": list(range(N)), "event_timestamp": [now] * N} + ) + results = _run_all(conn_str, fv_names, M, entity_df, client) + finally: + client.close() + + _print_table("scale_K", params, results) + _append_csv("scale_K", params, results) + + +# ============================================================================= +# Interaction: N × P +# +# Varying N and P together reveals whether native's P-independence holds across +# entity scales. If native is truly P-independent, (N=2000,P=20) should take +# roughly 4× longer than (N=500,P=20) — the same ratio as (N=2000,P=5) vs +# (N=500,P=5). For one and many the P dimension multiplies the N cost. +# +# Expected pattern (if native's index is effective): +# native time(N=500,P=20) ≈ time(N=500,P=5) ← P doesn't matter +# one time(N=500,P=20) ≈ 4× time(N=500,P=5) ← linear in P +# many time(N=500,P=20) ≈ 4× time(N=500,P=5) ← linear in P +# ============================================================================= + + +@_requires_docker +@pytest.mark.parametrize("N,P", N_P_GRID_SMOKE) +def test_interaction_N_P(conn_str: str, N: int, P: int) -> None: + """2×2 N×P grid. Hold M=M_BASE, K=K_BASE.""" + M, K = M_BASE, K_BASE + fv_names = [f"fv_{k}" for k in range(K)] + params = {"N": N, "M": M, "P": P, "K": K} + + client = MongoClient(conn_str) + try: + now = _reset_and_seed(client, fv_names, N, M, P) + entity_df = pd.DataFrame( + {"driver_id": list(range(N)), "event_timestamp": [now] * N} + ) + results = _run_all(conn_str, fv_names, M, entity_df, client) + finally: + client.close() + + _print_table("interaction_N_P", params, results) + _append_csv("interaction_N_P", params, results) + + +# ============================================================================= +# Stress: large N — memory scaling and OOM boundary +# +# At large N the memory difference between the implementations becomes stark: +# many loads N×P documents → O(N×P×M) bytes on the client. +# A MemoryError (or SKIPPED if projected to SIGKILL) is expected. +# one fetches via CHUNK_SIZE-entity batches → O(CHUNK_SIZE×P×M) peak, +# independent of total N. Must always complete. +# native fetches exactly 1 document per entity row → O(N×M) data returned, +# but the server-side $lookup + index scan is O(N) not O(N×P). +# At N=200k the $documents BSON payload (~10–15 MB) approaches +# MongoDB's 16 MB message limit; very large N may require chunking. +# ============================================================================= + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) +@pytest.mark.parametrize("N", N_STRESS) +def test_stress_scale_N(conn_str: str, N: int) -> None: + """Large-N stress test. all three implementations; OOM guard for 'many'.""" + M, P, K = M_STRESS, P_STRESS, K_BASE + fv_names = [f"fv_{k}" for k in range(K)] + params = {"N": N, "M": M, "P": P, "K": K} + total_docs = N * P + + client = MongoClient(conn_str) + try: + print( + f"\n[stress] Generating {total_docs:,} docs " + f"({N:,} entities × {P} rows × {M} features)…" + ) + now = _reset_and_seed(client, fv_names, N, M, P) + entity_df = pd.DataFrame( + {"driver_id": list(range(N)), "event_timestamp": [now] * N} + ) + results = _run_all( + conn_str, + fv_names, + M, + entity_df, + client, + skip_many_if_oom=True, + N=N, + P=P, + ) + finally: + client.close() + + _print_table("stress_scale_N", params, results) + _append_csv("stress_scale_N", params, results) + print(f" Total docs: {total_docs:,} | Raw ≈ {total_docs * M * 8 / 1e6:.0f} MB") + + assert results["one"].status == "OK", ( + f"'one' must complete at any scale via chunked fetching — got {results['one'].status}" + ) + + +# ============================================================================= +# Stress: OOM crossover — all three on the same large dataset +# +# Runs all three implementations on a single fixed scale chosen to put 'many' +# well into OOM territory on a ≤32 GB machine while 'one' and 'native' finish. +# +# Scale: OOM_N × OOM_P × OOM_M total float values in MongoDB. +# +# many projected ≈ _MANY_BYTES_PER_FLOAT × OOM_N × OOM_P × OOM_M MB +# → expected OOM or SKIPPED on ≤32 GB machines +# one CHUNK_SIZE-bounded → completes with ~several hundred MB +# native O(N×M) result → completes, but may be slow at OOM_N=200k +# ============================================================================= + +OOM_N = 200_000 +OOM_M = 100 +OOM_P = 5 + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) +def test_stress_oom_crossover(conn_str: str) -> None: + """OOM crossover demonstration — 'many' OOMs where 'one' and 'native' succeed.""" + N, M, P, K = OOM_N, OOM_M, OOM_P, K_BASE + fv_names = [f"fv_{k}" for k in range(K)] + total_docs = N * P + params = {"N": N, "M": M, "P": P, "K": K} + + print( + f"\n{'=' * 72}\n" + f"OOM CROSSOVER DEMO\n" + f" Scale : {N:,} entities × {P} rows × {M} features\n" + f" Docs : {total_docs:,} | raw ≈ {total_docs * M * 8 / 1e6:.0f} MB\n" + f" many projected peak ≈ {_projected_many_mb(N, P, M):,.0f} MB\n" + f"{'=' * 72}" + ) + + client = MongoClient(conn_str) + try: + print(f"Generating {total_docs:,} docs…") + now = _reset_and_seed(client, fv_names, N, M, P) + entity_df = pd.DataFrame( + {"driver_id": list(range(N)), "event_timestamp": [now] * N} + ) + results = _run_all( + conn_str, + fv_names, + M, + entity_df, + client, + skip_many_if_oom=True, + N=N, + P=P, + ) + finally: + client.close() + + _print_table("stress_oom_crossover", params, results) + _append_csv("stress_oom_crossover", params, results) + + assert results["one"].status == "OK", ( + f"'one' must complete regardless of scale — got {results['one'].status}" + ) From 448a698f04a5a235d08b13f579249f77d96d2a9b Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Sun, 19 Apr 2026 15:21:09 -0400 Subject: [PATCH 50/76] =?UTF-8?q?Add=20mongodb=5Fagg=20offline=20store=20?= =?UTF-8?q?=E2=80=94=20$match+$sort+$group,=20O(log=20P)=20without=20$look?= =?UTF-8?q?up/$expr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mongodb_agg.py implements a fourth offline store that avoids the $expr+$lookup COLLSCAN limitation of mongodb_native. Uses a compound-index-backed $match→$sort→$group pipeline (scoring path) or $match+merge_asof (training path). K-collapse batches FVs sharing the same join key into a single round-trip. Confirmed properties: O(log P) (7% over 20× P on Atlas), ~2 MB memory at N=500/P=5, K-collapse reduces fan-out cost vs native (2.8× at K=3 vs native's 5×). Also: - benchmark_sweep.py: add agg as fourth column, support MONGODB_URI env var for running against an external cluster without a testcontainer - design/benchmark-sweep-findings.md: add agg implementation section, K-sweep results including bug-fixed K=3, updated summary table Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_agg.py | 793 ++++++++++++++++++ .../mongodb_offline_store/benchmark_sweep.py | 36 +- 2 files changed, 826 insertions(+), 3 deletions(-) create mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py new file mode 100644 index 00000000000..a5c952682cc --- /dev/null +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py @@ -0,0 +1,793 @@ +# Copyright 2026 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +MongoDB Offline Store — Aggregation Implementation (mongodb_agg). + +Single-collection schema identical to mongodb_one. The query core differs +in two ways: + +1. K-collapse: feature views that share the same join key set are batched + into a single ``$match + $sort`` aggregation instead of K separate find + queries. Reduces round-trips from K to |unique join key signatures|. + +2. Server-side deduplication (scoring path): when entity_df has unique + entity IDs the aggregation adds a ``$group`` stage that returns at most + one document per (entity_id, feature_view) pair — O(N×K) transfer + instead of O(N×P×K). The compound index backs the entire pipeline, + making per-entity cost O(log P) rather than O(P). + + For training data (repeated entity IDs at different timestamps) the + ``$group`` optimisation is skipped and ``merge_asof`` is used instead, + matching mongodb_one behaviour. + +Index (created lazily on first use):: + + (entity_id ASC, feature_view ASC, event_timestamp DESC, created_at DESC) +""" + +import warnings +from collections import defaultdict +from datetime import datetime, timezone +from typing import ( + Any, + Callable, + Dict, + Generator, + List, + Optional, + Set, + Tuple, + Union, +) + +import pandas as pd +import pyarrow + +try: + from pymongo import ASCENDING, DESCENDING, MongoClient +except ImportError: + MongoClient = None # type: ignore[assignment,misc] + +from pydantic import StrictStr + +from feast.data_source import DataSource +from feast.errors import ( + DataSourceNoNameException, + FeastExtrasDependencyImportError, + SavedDatasetLocationAlreadyExists, +) +from feast.feature_view import FeatureView +from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.offline_store import ( + OfflineStore, + RetrievalJob, + RetrievalMetadata, +) +from feast.infra.offline_stores.offline_utils import ( + get_expected_join_keys, + infer_event_timestamp_from_entity_df, +) +from feast.infra.registry.base_registry import BaseRegistry +from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto +from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto +from feast.protos.feast.types.Value_pb2 import Value as ValueProto +from feast.repo_config import FeastConfigBaseModel, RepoConfig +from feast.saved_dataset import SavedDatasetStorage +from feast.type_map import mongodb_to_feast_value_type +from feast.value_type import ValueType + +# Cache: avoid re-creating the compound index on every call +_indexes_ensured: Set[str] = set() + + +# --------------------------------------------------------------------------- +# Config +# --------------------------------------------------------------------------- + + +class MongoDBOfflineStoreAggConfig(FeastConfigBaseModel): + """Configuration for the MongoDB agg offline store (single shared collection).""" + + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_agg.MongoDBOfflineStoreAgg" + + connection_string: StrictStr = "mongodb://localhost:27017" + """MongoDB connection URI""" + + database: StrictStr = "feast" + """MongoDB database name""" + + collection: StrictStr = "feature_history" + """Single collection shared by all feature views""" + + +# --------------------------------------------------------------------------- +# Data source +# --------------------------------------------------------------------------- + + +class MongoDBSourceAgg(DataSource): + """Data source for the aggregation offline store. + + Identical semantics to MongoDBSourceOne: the ``name`` field is used as + the ``feature_view`` discriminator inside the single shared collection. + """ + + def __init__( + self, + name: Optional[str] = None, + timestamp_field: str = "event_timestamp", + created_timestamp_column: str = "created_at", + field_mapping: Optional[Dict[str, str]] = None, + description: Optional[str] = "", + tags: Optional[Dict[str, str]] = None, + owner: Optional[str] = "", + ): + if name is None: + raise DataSourceNoNameException() + super().__init__( + name=name, + timestamp_field=timestamp_field, + created_timestamp_column=created_timestamp_column, + field_mapping=field_mapping or {}, + description=description, + tags=tags or {}, + owner=owner, + ) + + @property + def feature_view_name(self) -> str: + return self.name + + def validate(self, config: RepoConfig) -> None: + pass + + @staticmethod + def from_proto(data_source: DataSourceProto) -> "MongoDBSourceAgg": + custom = data_source.custom_options + return MongoDBSourceAgg( + name=custom.configuration.get("name", ""), + timestamp_field=custom.configuration.get( + "timestamp_field", "event_timestamp" + ), + created_timestamp_column=custom.configuration.get( + "created_timestamp_column", "created_at" + ), + description=data_source.description, + tags=dict(data_source.tags), + owner=data_source.owner, + ) + + def to_proto(self) -> DataSourceProto: + import json + + options = DataSourceProto.CustomSourceOptions( + configuration=json.dumps( + { + "name": self.name, + "timestamp_field": self.timestamp_field, + "created_timestamp_column": self.created_timestamp_column, + } + ) + ) + return DataSourceProto( + name=self.name, + type=DataSourceProto.CUSTOM_SOURCE, + description=self.description, + tags=self.tags, + owner=self.owner, + timestamp_field=self.timestamp_field, + custom_options=options, + ) + + def get_table_query_string(self) -> str: + return self.name + + @staticmethod + def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: + return mongodb_to_feast_value_type + + +# --------------------------------------------------------------------------- +# Retrieval job +# --------------------------------------------------------------------------- + + +class MongoDBAggRetrievalJob(RetrievalJob): + def __init__( + self, + query_fn: Callable[[], pyarrow.Table], + full_feature_names: bool, + config: RepoConfig, + metadata: Optional[RetrievalMetadata] = None, + ): + self._query_fn = query_fn + self._full_feature_names = full_feature_names + self._config = config + self._metadata = metadata + + @property + def full_feature_names(self) -> bool: + return self._full_feature_names + + @property + def metadata(self) -> Optional[RetrievalMetadata]: + return self._metadata + + def _to_arrow_internal(self, timeout: Optional[int] = None) -> pyarrow.Table: + return self._query_fn() + + def to_df(self, timeout: Optional[int] = None) -> pd.DataFrame: + return self._to_arrow_internal(timeout=timeout).to_pandas() + + def persist( + self, + storage: SavedDatasetStorage, + allow_overwrite: bool = False, + timeout: Optional[int] = None, + ) -> None: + if isinstance(storage, SavedDatasetStorage): + if hasattr(storage, "path"): + path = storage.path + if not allow_overwrite: + import os + + if os.path.exists(path): + raise SavedDatasetLocationAlreadyExists(save_path=path) + self.to_df().to_parquet(path) + + +# --------------------------------------------------------------------------- +# Helpers (copied from mongodb_one.py) +# --------------------------------------------------------------------------- + + +def _fetch_documents( + client: Any, db_name: str, collection_name: str, pipeline: List[Dict] +) -> List[Dict]: + db = client[db_name] + return list(db[collection_name].aggregate(pipeline)) + + +def _serialize_entity_key_from_row( + row: pd.Series, + join_keys: List[str], + entity_key_version: int, + join_key_types: Dict[str, ValueType], +) -> bytes: + entity_key = EntityKeyProto() + for jk in join_keys: + val = row[jk] + entity_key.join_keys.append(jk) + proto_val = ValueProto() + vtype = join_key_types.get(jk, ValueType.UNKNOWN) + if vtype in (ValueType.INT32, ValueType.INT64) or isinstance(val, int): + proto_val.int64_val = int(val) + elif vtype in (ValueType.STRING,) or isinstance(val, str): + proto_val.string_val = str(val) + elif isinstance(val, float): + proto_val.double_val = float(val) + else: + proto_val.int64_val = int(val) + entity_key.entity_values.append(proto_val) + return serialize_entity_key(entity_key, entity_key_version) + + +# --------------------------------------------------------------------------- +# Offline store +# --------------------------------------------------------------------------- + + +class MongoDBOfflineStoreAgg(OfflineStore): + """MongoDB offline store using a single collection and grouped aggregation. + + Improves on MongoDBOfflineStoreOne by: + - Collapsing K feature-view queries into one aggregation per join-key group + - Using server-side ``$group`` (O(log P) with index) for the scoring path + """ + + @staticmethod + def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: + """Create the compound index that enables O(log P) per-entity lookups.""" + collection = client[db_name][collection_name] + target_key = [ + ("entity_id", ASCENDING), + ("feature_view", ASCENDING), + ("event_timestamp", DESCENDING), + ("created_at", DESCENDING), + ] + existing = collection.index_information() + for idx_info in existing.values(): + if idx_info.get("key") == target_key: + return + collection.create_index(target_key, name="entity_fv_ts_idx", background=True) + + @staticmethod + def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: + if MongoClient is None: + raise FeastExtrasDependencyImportError("pymongo", "mongodb") + conn_str = config.offline_store.connection_string + db_name = config.offline_store.database + collection = config.offline_store.collection + cache_key = f"{conn_str}/{db_name}/{collection}" + client = MongoClient(conn_str) + if cache_key not in _indexes_ensured: + MongoDBOfflineStoreAgg._ensure_indexes(client, db_name, collection) + _indexes_ensured.add(cache_key) + return client + + @staticmethod + def pull_latest_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str], + start_date: datetime, + end_date: datetime, + ) -> RetrievalJob: + if not isinstance(data_source, MongoDBSourceAgg): + raise ValueError( + f"MongoDBOfflineStoreAgg expected MongoDBSourceAgg, " + f"got {type(data_source).__name__!r}." + ) + warnings.warn( + "MongoDB offline store (agg) is in preview. API may change without notice.", + RuntimeWarning, + ) + db_name = config.offline_store.database + collection = config.offline_store.collection + feature_view_name = data_source.feature_view_name + start_utc = start_date.astimezone(tz=timezone.utc) + end_utc = end_date.astimezone(tz=timezone.utc) + + project_stage: Dict[str, Any] = { + "_id": 0, + "entity_id": "$doc.entity_id", + "event_timestamp": "$doc.event_timestamp", + } + if created_timestamp_column: + project_stage["created_at"] = "$doc.created_at" + for feat in feature_name_columns: + project_stage[feat] = f"$doc.features.{feat}" + + pipeline: List[Dict[str, Any]] = [ + { + "$match": { + "feature_view": feature_view_name, + "event_timestamp": {"$gte": start_utc, "$lte": end_utc}, + } + }, + {"$sort": {"entity_id": 1, "event_timestamp": -1, "created_at": -1}}, + {"$group": {"_id": "$entity_id", "doc": {"$first": "$$ROOT"}}}, + {"$project": project_stage}, + ] + + def _run() -> pyarrow.Table: + client = MongoDBOfflineStoreAgg._get_client_and_ensure_indexes(config) + try: + docs = _fetch_documents(client, db_name, collection, pipeline) + if not docs: + return pyarrow.Table.from_pydict({}) + df = pd.DataFrame(docs) + if not df.empty and "event_timestamp" in df.columns: + if df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime( + df["event_timestamp"], utc=True + ) + return pyarrow.Table.from_pandas(df, preserve_index=False) + finally: + client.close() + + return MongoDBAggRetrievalJob( + query_fn=_run, full_feature_names=False, config=config + ) + + @staticmethod + def pull_all_from_table_or_query( + config: RepoConfig, + data_source: DataSource, + join_key_columns: List[str], + feature_name_columns: List[str], + timestamp_field: str, + created_timestamp_column: Optional[str] = None, + start_date: Optional[datetime] = None, + end_date: Optional[datetime] = None, + ) -> RetrievalJob: + if not isinstance(data_source, MongoDBSourceAgg): + raise ValueError( + f"MongoDBOfflineStoreAgg expected MongoDBSourceAgg, " + f"got {type(data_source).__name__!r}." + ) + warnings.warn( + "MongoDB offline store (agg) is in preview. API may change without notice.", + RuntimeWarning, + ) + db_name = config.offline_store.database + collection = config.offline_store.collection + feature_view_name = data_source.feature_view_name + match_filter: Dict[str, Any] = {"feature_view": feature_view_name} + if start_date or end_date: + ts_filter: Dict[str, Any] = {} + if start_date: + ts_filter["$gte"] = start_date.astimezone(tz=timezone.utc) + if end_date: + ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) + match_filter["event_timestamp"] = ts_filter + project_stage = {"_id": 0, "entity_id": 1, "event_timestamp": 1} + if created_timestamp_column: + project_stage["created_at"] = 1 + for feat in feature_name_columns: + project_stage[feat] = f"$features.{feat}" + pipeline = [{"$match": match_filter}, {"$project": project_stage}] + + def _run() -> pyarrow.Table: + client = MongoDBOfflineStoreAgg._get_client_and_ensure_indexes(config) + try: + docs = _fetch_documents(client, db_name, collection, pipeline) + if not docs: + return pyarrow.Table.from_pydict({}) + df = pd.DataFrame(docs) + if not df.empty and "event_timestamp" in df.columns: + if df["event_timestamp"].dt.tz is None: + df["event_timestamp"] = pd.to_datetime( + df["event_timestamp"], utc=True + ) + return pyarrow.Table.from_pandas(df, preserve_index=False) + finally: + client.close() + + return MongoDBAggRetrievalJob( + query_fn=_run, full_feature_names=False, config=config + ) + + @staticmethod + def get_historical_features( + config: RepoConfig, + feature_views: List[FeatureView], + feature_refs: List[str], + entity_df: Union[pd.DataFrame, str], + registry: BaseRegistry, + project: str, + full_feature_names: bool = False, + ) -> RetrievalJob: + """Fetch historical features using grouped aggregation. + + Groups feature views by join key signature so that FVs sharing the + same entity key are handled in a single MongoDB aggregation instead + of K separate queries. + + Scoring path (unique entity IDs in entity_df): + Uses ``$match + $sort + $group`` — server returns at most one + document per (entity_id, feature_view). The compound index + makes per-entity cost O(log P). Python post-filters the result. + + Training path (repeated entity IDs at different timestamps): + Omits ``$group`` and uses ``merge_asof`` in Python, matching + mongodb_one behaviour but still with K-collapsed queries. + """ + if isinstance(entity_df, str): + raise ValueError( + "MongoDBOfflineStoreAgg does not support SQL entity_df strings." + ) + warnings.warn( + "MongoDB offline store (agg) is in preview. API may change without notice.", + RuntimeWarning, + ) + + db_name = config.offline_store.database + feature_collection = config.offline_store.collection + entity_key_version = config.entity_key_serialization_version + + entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) + event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) + + fv_to_features: Dict[str, List[str]] = defaultdict(list) + for ref in feature_refs: + fv_name, feat_name = ref.split(":", 1) + fv_to_features[fv_name].append(feat_name) + + fv_by_name = {fv.name: fv for fv in feature_views} + fv_join_keys_by_name: Dict[str, List[str]] = { + fv.name: get_expected_join_keys(project, [fv], registry) + for fv in feature_views + } + fv_join_key_types_by_name: Dict[str, Dict[str, ValueType]] = { + fv.name: { + fv.projection.join_key_map.get( + ec.name, ec.name + ): ec.dtype.to_value_type() + for ec in fv.entity_columns + } + for fv in feature_views + } + + CHUNK_SIZE = 50_000 + MONGO_BATCH_SIZE = 10_000 + + def _chunk_dataframe( + df: pd.DataFrame, size: int + ) -> Generator[pd.DataFrame, None, None]: + for i in range(0, len(df), size): + yield df.iloc[i : i + size] + + def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: + result = entity_subset_df.copy() + if not pd.api.types.is_datetime64_any_dtype(result[event_timestamp_col]): + result[event_timestamp_col] = pd.to_datetime( + result[event_timestamp_col], utc=True + ) + elif result[event_timestamp_col].dt.tz is None: + result[event_timestamp_col] = pd.to_datetime( + result[event_timestamp_col], utc=True + ) + + max_ts = result[event_timestamp_col].max() + + # Detect scoring vs training path once per chunk. + # Scoring: unique entity IDs across ALL join key combinations. + # Training: repeated entity IDs → must use merge_asof. + all_entity_id_cols = list( + {jk for jks in fv_join_keys_by_name.values() for jk in jks} + & set(result.columns) + ) + scoring_path = result[all_entity_id_cols].drop_duplicates().shape[0] == len( + result + ) + + # Group FVs by join key signature to collapse K → |unique key sets| + fv_groups: Dict[Tuple[str, ...], List[str]] = defaultdict(list) + for fv_name in fv_to_features: + sig = tuple(sorted(fv_join_keys_by_name[fv_name])) + fv_groups[sig].append(fv_name) + + for join_key_sig, group_fv_names in fv_groups.items(): + join_keys = list(join_key_sig) + # Use the type map from the first FV in the group (keys are shared) + key_types = fv_join_key_types_by_name[group_fv_names[0]] + + result["_fv_entity_id"] = result.apply( + lambda row: _serialize_entity_key_from_row( + row, join_keys, entity_key_version, key_types + ), + axis=1, + ) + unique_entity_ids = result["_fv_entity_id"].unique().tolist() + + # Use the most conservative TTL across the group for the lower bound + ttls = [ + fv_by_name[n].ttl + for n in group_fv_names + if fv_by_name.get(n) and fv_by_name[n].ttl + ] + ts_filter: Dict[str, Any] = {"$lte": max_ts} + if ttls: + ts_filter["$gte"] = max_ts - min(ttls) + + all_docs: List[Dict] = [] + for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): + batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] + + if scoring_path: + # Server-side dedup: one doc per (entity_id, feature_view). + # The compound index backs $match→$sort→$group entirely. + pipeline: List[Dict] = [ + { + "$match": { + "entity_id": {"$in": batch_ids}, + "feature_view": {"$in": group_fv_names}, + "event_timestamp": ts_filter, + } + }, + { + "$sort": { + "entity_id": 1, + "feature_view": 1, + "event_timestamp": -1, + "created_at": -1, + } + }, + { + "$group": { + "_id": { + "eid": "$entity_id", + "fv": "$feature_view", + }, + "event_timestamp": {"$first": "$event_timestamp"}, + "features": {"$first": "$features"}, + "created_at": {"$first": "$created_at"}, + } + }, + # Reshape to flat document matching the training-path schema + { + "$project": { + "_id": 0, + "entity_id": "$_id.eid", + "feature_view": "$_id.fv", + "event_timestamp": 1, + "features": 1, + "created_at": 1, + } + }, + ] + else: + # Training path: fetch all docs in window; merge_asof + # handles per-row PIT precision. + pipeline = [ + { + "$match": { + "entity_id": {"$in": batch_ids}, + "feature_view": {"$in": group_fv_names}, + "event_timestamp": ts_filter, + } + }, + ] + + all_docs.extend(list(coll.aggregate(pipeline))) + + # Split returned docs by feature_view, then process per FV + docs_by_fv: Dict[str, List[Dict]] = defaultdict(list) + for doc in all_docs: + docs_by_fv[doc["feature_view"]].append(doc) + + for fv_name in group_fv_names: + features = fv_to_features[fv_name] + fv = fv_by_name.get(fv_name) + fv_docs = docs_by_fv.get(fv_name, []) + + if not fv_docs: + for feat in features: + col = f"{fv_name}__{feat}" if full_feature_names else feat + result[col] = None + continue + + fv_df = pd.DataFrame(fv_docs) + fv_df = fv_df.rename(columns={"entity_id": "_fv_entity_id"}) + + if "features" in fv_df.columns: + for feat in features: + fv_df[feat] = fv_df["features"].apply( + lambda d, f=feat: ( + d.get(f) if isinstance(d, dict) else None + ) + ) + fv_df = fv_df.drop(columns=["features"]) + + if fv_df["event_timestamp"].dt.tz is None: + fv_df["event_timestamp"] = pd.to_datetime( + fv_df["event_timestamp"], utc=True + ) + + if scoring_path: + # Dict-lookup join + post-filter + ts_map: Dict[bytes, Any] = { + bytes(r["_fv_entity_id"]): r["event_timestamp"] + for _, r in fv_df.iterrows() + } + feat_map: Dict[bytes, Dict] = {} + for _, r in fv_df.iterrows(): + eid = bytes(r["_fv_entity_id"]) + feat_map[eid] = {f: r.get(f) for f in features} + + for feat in features: + col = f"{fv_name}__{feat}" if full_feature_names else feat + result[col] = result.apply( + lambda row, f=feat: ( + feat_map.get(bytes(row["_fv_entity_id"]), {}).get(f) + if ( + bytes(row["_fv_entity_id"]) in ts_map + and ts_map[bytes(row["_fv_entity_id"])] + <= row[event_timestamp_col] + and ( + fv is None + or fv.ttl is None + or ts_map[bytes(row["_fv_entity_id"])] + >= row[event_timestamp_col] - fv.ttl + ) + ) + else None + ), + axis=1, + ) + else: + # merge_asof path (training data) + result = result.sort_values(event_timestamp_col).reset_index( + drop=True + ) + fv_df = fv_df.sort_values("event_timestamp").reset_index( + drop=True + ) + merge_cols = ["_fv_entity_id", "event_timestamp"] + [ + f for f in features if f in fv_df.columns + ] + fv_df_subset = fv_df[ + [c for c in merge_cols if c in fv_df.columns] + ].copy() + fv_df_subset = fv_df_subset.rename( + columns={"event_timestamp": "_fv_ts"} + ) + fv_prefix = f"__fv_{fv_name}__" + fv_df_subset = fv_df_subset.rename( + columns={ + f: f"{fv_prefix}{f}" + for f in features + if f in fv_df_subset.columns + } + ) + result = pd.merge_asof( + result, + fv_df_subset, + left_on=event_timestamp_col, + right_on="_fv_ts", + by="_fv_entity_id", + direction="backward", + ) + if fv and fv.ttl: + cutoff = result[event_timestamp_col] - fv.ttl + stale = result["_fv_ts"] < cutoff + for feat in features: + tc = f"{fv_prefix}{feat}" + if tc in result.columns: + result.loc[stale, tc] = None + for feat in features: + tc = f"{fv_prefix}{feat}" + col = f"{fv_name}__{feat}" if full_feature_names else feat + if tc in result.columns: + if col in result.columns: + result = result.drop(columns=[col]) + result = result.rename(columns={tc: col}) + elif col not in result.columns: + result[col] = None + result = result.drop(columns=["_fv_ts"], errors="ignore") + + # Drop the group's shared entity key column after all FVs processed + result = result.drop(columns=["_fv_entity_id"], errors="ignore") + + return result + + def _run() -> pyarrow.Table: + working_df = entity_df.copy() + working_df["_row_idx"] = range(len(working_df)) + + client = MongoDBOfflineStoreAgg._get_client_and_ensure_indexes(config) + try: + coll = client[db_name][feature_collection] + if len(working_df) <= CHUNK_SIZE: + result_df = _run_single(working_df, coll) + else: + chunks = [ + _run_single(chunk, coll) + for chunk in _chunk_dataframe(working_df, CHUNK_SIZE) + ] + result_df = pd.concat(chunks, ignore_index=True) + finally: + client.close() + + result_df = result_df.sort_values("_row_idx").reset_index(drop=True) + result_df = result_df.drop(columns=["_row_idx"], errors="ignore") + + if not result_df.empty and event_timestamp_col in result_df.columns: + if result_df[event_timestamp_col].dt.tz is None: + result_df[event_timestamp_col] = pd.to_datetime( + result_df[event_timestamp_col], utc=True + ) + + return pyarrow.Table.from_pandas(result_df, preserve_index=False) + + return MongoDBAggRetrievalJob( + query_fn=_run, + full_feature_names=full_feature_names, + config=config, + ) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py index 0259ce4e0aa..896cf005864 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py @@ -66,6 +66,11 @@ from feast import Entity, FeatureView, Field from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_agg import ( + MongoDBOfflineStoreAgg, + MongoDBOfflineStoreAggConfig, + MongoDBSourceAgg, +) from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( MongoDBOfflineStoreMany, MongoDBOfflineStoreManyConfig, @@ -220,6 +225,20 @@ class StoreImpl: timestamp_field="event_timestamp", ), ), + StoreImpl( + id="agg", + store_class=MongoDBOfflineStoreAgg, + make_offline_config=lambda conn: MongoDBOfflineStoreAggConfig( + connection_string=conn, + database=BENCH_DB, + collection=BENCH_FH, + ), + make_source=lambda fv_name: MongoDBSourceAgg( + name=fv_name, + timestamp_field="event_timestamp", + created_timestamp_column="created_at", + ), + ), ] # --------------------------------------------------------------------------- @@ -537,7 +556,7 @@ def _query(impl=impl, config=config, fvs=fvs): def _print_table(test: str, params: Dict, results: Dict[str, BenchResult]) -> None: """Print a side-by-side results table to stdout.""" - impl_ids = [impl.id for impl in ALL_IMPLS] + impl_ids = list(results.keys()) N = params.get("N", 0) title = ( @@ -632,7 +651,12 @@ def _append_csv(test: str, params: Dict, results: Dict[str, BenchResult]) -> Non @pytest.fixture(scope="module") -def mongodb_container() -> Generator[MongoDbContainer, None, None]: +def mongodb_container() -> Generator[Optional[MongoDbContainer], None, None]: + import os + + if os.environ.get("MONGODB_URI"): + yield None # external cluster — no container needed + return container = MongoDbContainer( "mongo:latest", username="test", @@ -644,7 +668,13 @@ def mongodb_container() -> Generator[MongoDbContainer, None, None]: @pytest.fixture -def conn_str(mongodb_container: MongoDbContainer) -> str: +def conn_str(mongodb_container: Optional[MongoDbContainer]) -> str: + import os + + uri = os.environ.get("MONGODB_URI") + if uri: + return uri + assert mongodb_container is not None port = mongodb_container.get_exposed_port(27017) return f"mongodb://test:test@localhost:{port}" # pragma: allowlist secret From 810b7d0894294ce96c7dbc66d20b83003726e2dd Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Sun, 19 Apr 2026 18:39:11 -0400 Subject: [PATCH 51/76] Vectorize agg scoring path, add upfront index build, ignore design/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mongodb_agg.py: - Replace O(N×M) apply(axis=1) scoring-path join with a vectorized merge + boolean mask — 4–6× faster at stress scale (N=50K: 209s → 28s) - Expand features dict via pd.json_normalize instead of per-feature apply - Both changes confirmed correct: K smoke tests pass, stress tier passes benchmark_sweep.py: - _reset_and_seed now creates the compound index synchronously after bulk insert so queries never race against a background index build - Added _COMPOUND_IDX constant shared by the seeding step Stress tier results (N=200K, M=100): agg: 139s, 955 MB trace (fastest + lowest memory) native: 171s, 3208 MB trace one: 331s, 4044 MB trace many: SKIPPED (13 GB projected) .gitignore: add design/ exclusion Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Casey Clements --- .gitignore | 3 + .../mongodb_offline_store/mongodb_agg.py | 66 ++++++++++--------- .../mongodb_offline_store/benchmark_sweep.py | 23 +++++-- 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index c0fdd58d338..4b291cefb56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ ### Scratch files ### scratch* +### Design / notes directory (local only) ### +design/ + ### Local Environment ### *local*.env tools diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py index a5c952682cc..d765ad043a1 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py @@ -657,11 +657,13 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: fv_df = fv_df.rename(columns={"entity_id": "_fv_entity_id"}) if "features" in fv_df.columns: + # Expand features dict in one vectorized pass + feat_expanded = pd.json_normalize(fv_df["features"].tolist()) for feat in features: - fv_df[feat] = fv_df["features"].apply( - lambda d, f=feat: ( - d.get(f) if isinstance(d, dict) else None - ) + fv_df[feat] = ( + feat_expanded[feat].values + if feat in feat_expanded.columns + else None ) fv_df = fv_df.drop(columns=["features"]) @@ -671,36 +673,40 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: ) if scoring_path: - # Dict-lookup join + post-filter - ts_map: Dict[bytes, Any] = { - bytes(r["_fv_entity_id"]): r["event_timestamp"] - for _, r in fv_df.iterrows() - } - feat_map: Dict[bytes, Dict] = {} - for _, r in fv_df.iterrows(): - eid = bytes(r["_fv_entity_id"]) - feat_map[eid] = {f: r.get(f) for f in features} + # Vectorized join: merge fv_df onto result by entity_id, + # then null out rows where the server returned a doc that + # is too recent (max_ts approximation) or TTL-stale. + fv_join_cols = ["_fv_entity_id", "event_timestamp"] + [ + f for f in features if f in fv_df.columns + ] + fv_join = fv_df[fv_join_cols].rename( + columns={"event_timestamp": "_fv_ts"} + ) + # left merge: entities with no match get NaN features + merged = result[["_fv_entity_id", event_timestamp_col]].merge( + fv_join, on="_fv_entity_id", how="left" + ) + + # Mask: fv doc is in the future relative to entity request + # time (max_ts overshoot) or outside TTL window. + future_mask = merged["_fv_ts"] > merged[event_timestamp_col] + if fv and fv.ttl: + ttl_mask = merged["_fv_ts"] < ( + merged[event_timestamp_col] - fv.ttl + ) + bad_mask = future_mask | ttl_mask + else: + bad_mask = future_mask for feat in features: col = f"{fv_name}__{feat}" if full_feature_names else feat - result[col] = result.apply( - lambda row, f=feat: ( - feat_map.get(bytes(row["_fv_entity_id"]), {}).get(f) - if ( - bytes(row["_fv_entity_id"]) in ts_map - and ts_map[bytes(row["_fv_entity_id"])] - <= row[event_timestamp_col] - and ( - fv is None - or fv.ttl is None - or ts_map[bytes(row["_fv_entity_id"])] - >= row[event_timestamp_col] - fv.ttl - ) - ) - else None - ), - axis=1, + vals = ( + merged[feat].copy() + if feat in merged.columns + else pd.Series([None] * len(merged), dtype=object) ) + vals[bad_mask | merged["_fv_ts"].isna()] = None + result[col] = vals.values else: # merge_asof path (training data) result = result.sort_values(event_timestamp_col).reset_index( diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py index 896cf005864..392d3d78777 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py @@ -316,6 +316,14 @@ def _seed_benchmark( return now +_COMPOUND_IDX = [ + ("entity_id", 1), + ("feature_view", 1), + ("event_timestamp", -1), + ("created_at", -1), +] + + def _reset_and_seed( client: MongoClient, fv_names: List[str], @@ -323,14 +331,21 @@ def _reset_and_seed( num_features: int, rows_per_entity: int, ) -> datetime: - """Drop benchmark collections, then seed fresh data for this run.""" + """Drop benchmark collections, seed fresh data, then build index. + + The compound index is created AFTER seeding so that the bulk insert is + not slowed by incremental index maintenance. All four implementations + share this index; building it once here means the first benchmark query + never has to wait for a concurrent background index build. + """ db = client[BENCH_DB] db[BENCH_FH].drop() for fv_name in fv_names: db[fv_name].drop() - return _seed_benchmark( - client, fv_names, num_entities, num_features, rows_per_entity - ) + now = _seed_benchmark(client, fv_names, num_entities, num_features, rows_per_entity) + # Build the compound index synchronously so queries are never forced to scan. + db[BENCH_FH].create_index(_COMPOUND_IDX, name="entity_fv_ts_idx") + return now # --------------------------------------------------------------------------- From 2a56c13ebb9ff9a2353e22ea7e9f4d7c02dcb5f2 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Mon, 20 Apr 2026 19:21:20 -0400 Subject: [PATCH 52/76] Adds offline_write_batch Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_agg.py | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py index d765ad043a1..9cb1b45ec20 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py @@ -797,3 +797,126 @@ def _run() -> pyarrow.Table: full_feature_names=full_feature_names, config=config, ) + + @staticmethod + def offline_write_batch( + config: RepoConfig, + feature_view: FeatureView, + table: pyarrow.Table, + progress: Optional[Callable[[int], Any]], + ) -> None: + """Write a batch of feature observations into the feature_history collection. + + Each row in *table* is stored as one document:: + + { + "entity_id": , + "feature_view": , + "features": {: , ...}, + "event_timestamp": , + "created_at": , + } + + Writes are append-only (no upsert). Conflict resolution at read time: + pull_latest picks the highest ``created_at``; the scoring path + ``$sort created_at DESC`` → ``$group $first`` also picks the highest. + + Args: + config: Feast repo configuration. + feature_view: The feature view being written; must have a + MongoDBSourceAgg batch source. + table: Arrow table with join key columns, feature columns, + ``event_timestamp``, and optionally ``created_at``. + progress: Optional callback invoked with the row count after each + batch insert. + """ + if not isinstance(feature_view.batch_source, MongoDBSourceAgg): + raise ValueError( + f"MongoDBOfflineStoreAgg.offline_write_batch expected a MongoDBSourceAgg " + f"batch source, got {type(feature_view.batch_source).__name__!r}." + ) + + entity_key_version = config.entity_key_serialization_version + db_name = config.offline_store.database + collection_name = config.offline_store.collection + + join_key_types: Dict[str, ValueType] = { + feature_view.projection.join_key_map.get( + ec.name, ec.name + ): ec.dtype.to_value_type() + for ec in feature_view.entity_columns + } + join_keys = list(join_key_types.keys()) + + timestamp_field = feature_view.batch_source.timestamp_field + created_ts_col: Optional[str] = ( + feature_view.batch_source.created_timestamp_column or None + ) + + reserved = set(join_keys) | {timestamp_field} + if created_ts_col: + reserved.add(created_ts_col) + feature_cols = [c for c in table.column_names if c not in reserved] + + df = table.to_pandas() + + for ts_col in [timestamp_field] + ([created_ts_col] if created_ts_col else []): + if ts_col in df.columns: + if not pd.api.types.is_datetime64_any_dtype(df[ts_col]): + df[ts_col] = pd.to_datetime(df[ts_col], utc=True) + elif df[ts_col].dt.tz is None: + df[ts_col] = df[ts_col].dt.tz_localize("UTC") + + df["_entity_id"] = df.apply( + lambda row: _serialize_entity_key_from_row( + row, join_keys, entity_key_version, join_key_types + ), + axis=1, + ) + + now = datetime.now(tz=timezone.utc) + + docs = [] + for _, row in df.iterrows(): + features: Dict[str, Any] = {} + for col in feature_cols: + val = row[col] + if pd.isna(val): + continue + if hasattr(val, "item"): + val = val.item() + features[col] = val + + created_at = now + if created_ts_col and created_ts_col in df.columns: + ct = row[created_ts_col] + if not pd.isna(ct): + created_at = ( + ct.to_pydatetime() if hasattr(ct, "to_pydatetime") else ct + ) + + docs.append( + { + "entity_id": row["_entity_id"], + "feature_view": feature_view.name, + "features": features, + "event_timestamp": ( + row[timestamp_field].to_pydatetime() + if hasattr(row[timestamp_field], "to_pydatetime") + else row[timestamp_field] + ), + "created_at": created_at, + } + ) + + client = MongoDBOfflineStoreAgg._get_client_and_ensure_indexes(config) + try: + coll = client[db_name][collection_name] + BATCH_SIZE = 10_000 + for i in range(0, len(docs), BATCH_SIZE): + batch = docs[i : i + BATCH_SIZE] + coll.insert_many(batch, ordered=False) + if progress: + progress(len(batch)) + finally: + client.close() From 7e35e6a33c24a40982e6ab66737a7ad6560a30db Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Mon, 20 Apr 2026 19:25:09 -0400 Subject: [PATCH 53/76] Adds detail to handling of K in benchmarks. Signed-off-by: Casey Clements --- .../mongodb_offline_store/benchmark_sweep.py | 101 +++++++++++++++++- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py index 392d3d78777..e8830886ef8 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py @@ -51,7 +51,7 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta from pathlib import Path -from typing import Any, Callable, Dict, Generator, List, Optional +from typing import Any, Callable, Dict, Generator, List, Optional, Set import pandas as pd import pytest @@ -517,6 +517,7 @@ def _run_all( entity_df: pd.DataFrame, mongo_client: Any, skip_many_if_oom: bool = False, + skip_impl_ids: Optional[Set[str]] = None, N: int = 0, P: int = 0, ) -> Dict[str, BenchResult]: @@ -525,9 +526,26 @@ def _run_all( results: Dict[str, BenchResult] = {} for impl in ALL_IMPLS: - # OOM guard for 'many' in stress scenarios + # Caller-supplied skip list (e.g. native at high K due to COLLSCAN × K issue) + if skip_impl_ids and impl.id in skip_impl_ids: + results[impl.id] = BenchResult( + elapsed_s=0, + trace_mb=0, + status="SKIPPED:excluded", + ) + continue + + # OOM guard for 'many' in stress scenarios. + # 'many' processes K feature views sequentially but does NOT release + # the merged result between iterations — the full K×N×M array + # accumulates. Multiply the per-FV projection by K before checking. if skip_many_if_oom and impl.id == "many" and N > 0 and P > 0: - safe, proj_mb, free_mb = _many_safe(N, P, num_features) + K = len(fv_names) + per_fv_mb = _projected_many_mb(N, P, num_features) + total_proj_mb = per_fv_mb * K + free_mb = _free_memory_mb() + safe = total_proj_mb < free_mb * 0.80 + proj_mb = total_proj_mb if not safe: results[impl.id] = BenchResult( elapsed_s=0, @@ -536,7 +554,7 @@ def _run_all( ) print( f" [many] SKIPPED — projected {proj_mb:.0f} MB " - f"> 80 % of {free_mb:.0f} MB free" + f"(K={K} × {per_fv_mb:.0f} MB/FV) > 80 % of {free_mb:.0f} MB free" ) continue @@ -996,3 +1014,78 @@ def test_stress_oom_crossover(conn_str: str) -> None: assert results["one"].status == "OK", ( f"'one' must complete regardless of scale — got {results['one'].status}" ) + + +# ============================================================================= +# Stress: K fan-out — K-collapse comparison +# +# All K feature views share the same driver_id join key. +# +# agg : K-collapses all K FVs into ONE $match+$sort+$group aggregation, +# regardless of K. Round trips = ceil(N / MONGO_BATCH_SIZE). +# one : issues K separate $match aggregations per batch. +# Round trips = K × ceil(N / MONGO_BATCH_SIZE). +# many : does K full per-FV collection scans, one per FV. +# native : SKIPPED at all K — it issues K independent $documents+$lookup +# pipelines, each doing a full COLLSCAN of the shared collection +# (COLLSCAN cost is O(K × N × total_docs) — impractical at K ≥ 10). +# +# Expected pattern: +# agg time ≈ constant as K grows (single aggregation, K-collapse) +# one time grows O(K) (K round trips) +# many time grows O(K) (K collection scans, large network transfer) +# ============================================================================= + +N_FAN_K = 2_000 +M_FAN_K = 100 +P_FAN_K = 5 +K_FAN_VALUES = [1, 10, 100] + + +@_requires_docker +@pytest.mark.slow +@pytest.mark.timeout(0) +@pytest.mark.parametrize("K", K_FAN_VALUES) +def test_stress_fan_K(conn_str: str, K: int) -> None: + """K fan-out stress test: measures K-collapse benefit of agg vs one/many. + + native is always skipped: it issues K independent $documents+$lookup pipelines + each doing a COLLSCAN of the shared collection. At K=10 with N=2000 (100 k docs) + this is already impractical; at K=100 it would never complete. + """ + N, M, P = N_FAN_K, M_FAN_K, P_FAN_K + total_docs = N * P * K + fv_names = [f"fv_{k}" for k in range(K)] + params = {"N": N, "M": M, "P": P, "K": K} + + client = MongoClient(conn_str) + try: + print( + f"\n[stress_fan_K] Seeding {total_docs:,} docs " + f"({N:,} entities × {P} rows × {M} features × {K} FVs)…" + ) + now = _reset_and_seed(client, fv_names, N, M, P) + entity_df = pd.DataFrame( + {"driver_id": list(range(N)), "event_timestamp": [now] * N} + ) + results = _run_all( + conn_str, + fv_names, + M, + entity_df, + client, + skip_many_if_oom=True, + skip_impl_ids={"native"}, # K COLLSCANs — impractical at K >= 10 + N=N, + P=P, + ) + finally: + client.close() + + _print_table("stress_fan_K", params, results) + _append_csv("stress_fan_K", params, results) + print(f" Total docs: {total_docs:,} | Raw ≈ {total_docs * M * 8 / 1e6:.0f} MB") + + assert results["agg"].status == "OK", ( + f"'agg' must complete — got {results['agg'].status}" + ) From fcbb8e1a51eb89d4743b3c07ca400d7d796327a5 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 21 Apr 2026 07:32:42 -0400 Subject: [PATCH 54/76] Adds missing typing. Signed-off-by: Casey Clements --- .../mongodb_offline_store/mongodb_agg.py | 53 ++++++++----------- .../mongodb_offline_store/mongodb_one.py | 2 +- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py index 9cb1b45ec20..7285e3fd0d8 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py @@ -39,7 +39,7 @@ import warnings from collections import defaultdict -from datetime import datetime, timezone +from datetime import datetime, timedelta, timezone from typing import ( Any, Callable, @@ -150,45 +150,41 @@ def __init__( def feature_view_name(self) -> str: return self.name + def source_type(self) -> DataSourceProto.SourceType.ValueType: + return DataSourceProto.CUSTOM_SOURCE + def validate(self, config: RepoConfig) -> None: pass @staticmethod def from_proto(data_source: DataSourceProto) -> "MongoDBSourceAgg": - custom = data_source.custom_options + assert data_source.HasField("custom_options") return MongoDBSourceAgg( - name=custom.configuration.get("name", ""), - timestamp_field=custom.configuration.get( - "timestamp_field", "event_timestamp" - ), - created_timestamp_column=custom.configuration.get( - "created_timestamp_column", "created_at" - ), + name=data_source.name, + timestamp_field=data_source.timestamp_field, + created_timestamp_column=data_source.created_timestamp_column, + field_mapping=dict(data_source.field_mapping), description=data_source.description, tags=dict(data_source.tags), owner=data_source.owner, ) - def to_proto(self) -> DataSourceProto: + def _to_proto_impl(self) -> DataSourceProto: import json - options = DataSourceProto.CustomSourceOptions( - configuration=json.dumps( - { - "name": self.name, - "timestamp_field": self.timestamp_field, - "created_timestamp_column": self.created_timestamp_column, - } - ) - ) return DataSourceProto( name=self.name, type=DataSourceProto.CUSTOM_SOURCE, + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_agg.MongoDBSourceAgg", + field_mapping=self.field_mapping, + custom_options=DataSourceProto.CustomSourceOptions( + configuration=json.dumps({"feature_view": self.name}).encode() + ), description=self.description, tags=self.tags, owner=self.owner, timestamp_field=self.timestamp_field, - custom_options=options, + created_timestamp_column=self.created_timestamp_column, ) def get_table_query_string(self) -> str: @@ -228,9 +224,6 @@ def metadata(self) -> Optional[RetrievalMetadata]: def _to_arrow_internal(self, timeout: Optional[int] = None) -> pyarrow.Table: return self._query_fn() - def to_df(self, timeout: Optional[int] = None) -> pd.DataFrame: - return self._to_arrow_internal(timeout=timeout).to_pandas() - def persist( self, storage: SavedDatasetStorage, @@ -244,7 +237,7 @@ def persist( import os if os.path.exists(path): - raise SavedDatasetLocationAlreadyExists(save_path=path) + raise SavedDatasetLocationAlreadyExists(location=path) self.to_df().to_parquet(path) @@ -321,7 +314,7 @@ def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: db_name = config.offline_store.database collection = config.offline_store.collection cache_key = f"{conn_str}/{db_name}/{collection}" - client = MongoClient(conn_str) + client: Any = MongoClient(conn_str) if cache_key not in _indexes_ensured: MongoDBOfflineStoreAgg._ensure_indexes(client, db_name, collection) _indexes_ensured.add(cache_key) @@ -426,7 +419,7 @@ def pull_all_from_table_or_query( if end_date: ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) match_filter["event_timestamp"] = ts_filter - project_stage = {"_id": 0, "entity_id": 1, "event_timestamp": 1} + project_stage: Dict[str, Any] = {"_id": 0, "entity_id": 1, "event_timestamp": 1} if created_timestamp_column: project_stage["created_at"] = 1 for feat in feature_name_columns: @@ -501,7 +494,7 @@ def get_historical_features( fv_by_name = {fv.name: fv for fv in feature_views} fv_join_keys_by_name: Dict[str, List[str]] = { - fv.name: get_expected_join_keys(project, [fv], registry) + fv.name: list(get_expected_join_keys(project, [fv], registry)) for fv in feature_views } fv_join_key_types_by_name: Dict[str, Dict[str, ValueType]] = { @@ -567,10 +560,10 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: unique_entity_ids = result["_fv_entity_id"].unique().tolist() # Use the most conservative TTL across the group for the lower bound - ttls = [ - fv_by_name[n].ttl + ttls: List[timedelta] = [ + ttl for n in group_fv_names - if fv_by_name.get(n) and fv_by_name[n].ttl + if fv_by_name.get(n) and (ttl := fv_by_name[n].ttl) is not None ] ts_filter: Dict[str, Any] = {"$lte": max_ts} if ttls: diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py index 6eafa8debd3..7b0e85477a4 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py @@ -900,7 +900,7 @@ def get_historical_features( # serialized bytes that never match stored documents when FVs have # heterogeneous join keys (e.g. FV1 uses driver_id, FV2 uses customer_id). fv_join_keys_by_name: Dict[str, List[str]] = { - fv.name: get_expected_join_keys(project, [fv], registry) + fv.name: list(get_expected_join_keys(project, [fv], registry)) for fv in feature_views } # Declared ValueType per join key, derived directly from entity_columns on From 6e8f5023a54a571c4f73d87e2c6a9f18dfe8ee93 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 23 Apr 2026 13:59:29 -0400 Subject: [PATCH 55/76] Consolidate MongoDB offline store to single implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the four-implementation experiment (native, one, many, agg) with a single production-ready mongodb.py. Key changes: - Rename mongodb_agg.py → mongodb.py; rename all public classes to drop the Agg suffix (MongoDBOfflineStore, MongoDBSource, MongoDBOfflineStoreConfig) - Add strict_pit flag to get_historical_features (False = real-time inference) - Fix INT32 entity key serialisation (was always writing int64_val) - Add on_demand_feature_views property to MongoDBRetrievalJob - Expose _CHUNK_SIZE / _MONGO_BATCH_SIZE at module level for testability - Update persist() to accept SavedDatasetFileStorage natively - Replace test_one/many/native/cross + benchmarks with test_mongodb.py (17 tests including strict_pit, chunk-boundary, and K-collapse coverage) - Wire MongoDBDataSourceCreator into repo_configuration; implement create_saved_dataset_destination via SavedDatasetFileStorage - Remove design/ from .gitignore Signed-off-by: Casey Clements --- .gitignore | 3 - .secrets.baseline | 6 +- .../contrib/mongodb_offline_store/README.md | 167 +- .../{mongodb_agg.py => mongodb.py} | 122 +- .../mongodb_offline_store/mongodb_many.py | 678 ------ .../mongodb_offline_store/mongodb_native.py | 1110 --------- .../mongodb_offline_store/mongodb_one.py | 1121 --------- .../mongodb_offline_store/benchmark.py | 2002 ----------------- .../mongodb_offline_store/benchmark_sweep.py | 1091 --------- .../mongodb_offline_store/test_cross.py | 796 ------- .../mongodb_offline_store/test_many.py | 778 ------- .../{test_one.py => test_mongodb.py} | 937 +++++--- .../mongodb_offline_store/test_native.py | 1111 --------- .../feature_repos/repo_configuration.py | 18 +- .../universal/data_sources/mongodb.py | 176 +- 15 files changed, 756 insertions(+), 9360 deletions(-) rename sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/{mongodb_agg.py => mongodb.py} (90%) delete mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py delete mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py delete mode 100644 sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py delete mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py delete mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py delete mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_cross.py delete mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py rename sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/{test_one.py => test_mongodb.py} (53%) delete mode 100644 sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_native.py diff --git a/.gitignore b/.gitignore index 4b291cefb56..c0fdd58d338 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ ### Scratch files ### scratch* -### Design / notes directory (local only) ### -design/ - ### Local Environment ### *local*.env tools diff --git a/.secrets.baseline b/.secrets.baseline index ae02873f833..7c6082ce59b 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1460,14 +1460,14 @@ "filename": "sdk/python/tests/universal/feature_repos/repo_configuration.py", "hashed_secret": "d90e76ef629fb00c95f4e84fec29fbda111e2392", "is_verified": false, - "line_number": 486 + "line_number": 478 }, { "type": "Secret Keyword", "filename": "sdk/python/tests/universal/feature_repos/repo_configuration.py", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 488 + "line_number": 480 } ], "sdk/python/tests/universal/feature_repos/universal/data_sources/file.py": [ @@ -1539,5 +1539,5 @@ } ] }, - "generated_at": "2026-03-20T20:55:36Z" + "generated_at": "2026-04-23T17:55:15Z" } diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md index db6318ee17d..6a30854969c 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/README.md @@ -1,161 +1,72 @@ # MongoDB Offline Store -Two MongoDB offline store implementations optimized for different use cases. +This offline store lets you train models and run batch scoring directly from it. +All feature views share a single collection (`feature_history`). Reads use +MongoDB aggregation pipelines with a compound index, so per-entity cost is +O(log n_observations) regardless of collection size, and K feature views with the same +entity key collapse into one round-trip instead of K (1 if your data shares a unique id.) -## Overview +## Schema -| Aspect | `MongoDBOfflineStoreMany` | `MongoDBOfflineStoreOne` | -|--------|---------------------------|--------------------------| -| Collections | One per FeatureView | Single shared collection | -| Schema | Flat documents | Nested `features` subdoc | -| Entity ID | Separate columns | Serialized bytes | -| Best for | Small-medium feature stores | Large feature stores | - -## MongoDBOfflineStoreMany (mongodb_many.py) - -**One collection per FeatureView** — each FeatureView maps to its own MongoDB collection. - -### Schema +All feature views share one collection (default: `feature_history`), discriminated by the `feature_view` field. ```javascript -// Collection: driver_stats +// Collection: feature_history { - "driver_id": 1001, + "entity_id": Binary("..."), // Serialized entity key (bytes) + "feature_view": "driver_stats", // Discriminator + "features": { // Nested subdocument + "trips_today": 5, + "rating": 4.8 + }, "event_timestamp": ISODate("2024-01-15T10:00:00Z"), - "created_at": ISODate("2024-01-15T10:00:01Z"), // Optional tie-breaker - "trips_today": 5, - "rating": 4.8 + "created_at": ISODate("2024-01-15T10:00:01Z") } ``` +## Index -Ties (same `event_timestamp`) are broken by `created_timestamp_column` if configured. - -### Configuration - -```yaml -offline_store: - type: feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many.MongoDBOfflineStoreMany - connection_string: mongodb://localhost:27017 - database: feast -``` - -### When to Use - -✅ **Small to medium feature stores** — loads entire collection into memory -✅ **Fast PIT joins** — Ibis memtables are highly optimized -✅ **Simple schema** — flat documents, easy to query directly -✅ **Per-collection indexes** — each FV can have tailored indexes - -⚠️ **Caution**: Loads ALL documents from each collection. May OOM on very large collections. - -## MongoDBOfflineStoreOne (mongodb_one.py) - -**Single shared collection** — all FeatureViews store data in one collection with a discriminator field. - -### Schema +The store creates one compound index lazily on first use. This index supports every query issued.. ```javascript -// Collection: feature_history (shared by all FVs) -{ - "entity_id": Binary("..."), // Serialized entity key - "feature_view": "driver_stats", // Discriminator - "features": { // Nested subdocument - "trips_today": 5, - "rating": 4.8 - }, - "event_timestamp": ISODate("2024-01-15T10:00:00Z"), - "created_at": ISODate("2024-01-15T10:00:01Z") -} -``` +db.feature_history.createIndex({ + "entity_id": 1, + "feature_view": 1, + "event_timestamp": -1, + "created_at": -1 +}) -### Configuration +``` +## Configuration ```yaml offline_store: - type: feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one.MongoDBOfflineStoreOne + type: feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStore connection_string: mongodb://localhost:27017 database: feast - collection: feature_history + collection: feature_history # optional, default: feature_history ``` -### When to Use - -✅ **Large feature stores** — filters by entity_id, doesn't load entire collection -✅ **Memory-safe** — processes in chunks, bounded memory usage -✅ **Schema consistency** — matches online store pattern -✅ **Efficient materialization** — MQL aggregation pipeline +## Key Features -⚠️ **Trade-off**: Slightly slower than Many for small workloads due to serialization overhead. +**Query-collapse** — Feature views that share the same join key set are grouped into a single MongoDB aggregation round-trip instead of one per feature view. Reduces round-trips from K to the number of unique join key signatures, often one. -## Performance Comparison +**Scoring path** — When `entity_df` contains unique entity IDs, a `$match + $sort + $group` pipeline performs server-side deduplication returning at most one document per `(entity_id, feature_view)`. The compound index makes per-entity cost O(log n_obs). -Benchmarks with 10 features, 3 historical rows per entity: +**Training path** — When `entity_df` contains repeated entity IDs at different timestamps, the `$group` stage is omitted and `pandas.merge_asof` performs per-row point-in-time joins optimized in C. -| Entity Rows | Many (time) | One (time) | Winner | -|-------------|-------------|------------|--------| -| 1,000 | 0.30s | 0.06s | One | -| 10,000 | 0.20s | 0.31s | Many | -| 100,000 | 1.51s | 5.22s | Many | -| 1,000,000 | 16.08s | 212s | Many | +**`strict_pit`** — `get_historical_features` accepts a `strict_pit` keyword argument (default `True`). With `strict_pit=True` (default, safe for training), documents whose timestamp is strictly after the entity request timestamp are returned as `NULL`. Set `strict_pit=False` for real-time inference where you always want the most recent observation. -### Memory Behavior -| Scenario | Many | One | -|----------|------|-----| -| Large feature collection, small entity_df | ❌ Loads all | ✅ Filters | -| Small feature collection, large entity_df | ✅ Fast | ⚠️ Slower | +## Writing Data -## Choosing an Implementation +Use `offline_write_batch` (called automatically by `feast materialize`) to write feature observations: -``` - ┌─────────────────────────────┐ - │ Is your feature collection │ - │ larger than available RAM? │ - └─────────────────────────────┘ - │ - ┌──────────┴──────────┐ - ▼ ▼ - YES NO - │ │ - ▼ ▼ - ┌───────────────┐ ┌───────────────┐ - │ Use ONE │ │ Use MANY │ - │ (memory-safe) │ │ (faster) │ - └───────────────┘ └───────────────┘ +```python +store.write_to_offline_store(feature_view_name, df) ``` -## Index Recommendations - -### Many (per-collection) - -Each collection should have an index on the join keys + timestamp: - -```javascript -// For a FeatureView with join key "driver_id" -db.driver_stats.createIndex({ - "driver_id": 1, // Join key(s) - "event_timestamp": -1 -}) - -// For a FeatureView with compound join keys -db.order_stats.createIndex({ - "customer_id": 1, - "order_id": 1, - "event_timestamp": -1 -}) -``` - -**Note**: The Many implementation auto-creates indexes during `pull_latest_from_table_or_query` (materialization). - -### One (shared collection) - -```javascript -db.feature_history.createIndex({ - "entity_id": 1, - "feature_view": 1, - "event_timestamp": -1 -}) -``` +Documents are appended; `pull_latest` and the scoring path select the highest `created_at` at read time. -The One implementation creates this index automatically on first use. +## Memory Behaviour +The store filters by entity key in `$match` rather than loading the entire collection. Memory usage is bounded by the number of unique entity IDs × documents per entity, not the total collection size. diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py similarity index 90% rename from sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py rename to sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 7285e3fd0d8..58ef15182b1 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_agg.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -13,7 +13,7 @@ # limitations under the License. """ -MongoDB Offline Store — Aggregation Implementation (mongodb_agg). +MongoDB Offline Store. Single-collection schema identical to mongodb_one. The query core differs in two ways: @@ -30,7 +30,7 @@ For training data (repeated entity IDs at different timestamps) the ``$group`` optimisation is skipped and ``merge_asof`` is used instead, - matching mongodb_one behaviour. + Index (created lazily on first use):: @@ -91,16 +91,20 @@ # Cache: avoid re-creating the compound index on every call _indexes_ensured: Set[str] = set() +# Chunk sizes — exposed at module level so tests can patch them. +_CHUNK_SIZE = 50_000 +_MONGO_BATCH_SIZE = 10_000 + # --------------------------------------------------------------------------- # Config # --------------------------------------------------------------------------- -class MongoDBOfflineStoreAggConfig(FeastConfigBaseModel): +class MongoDBOfflineStoreConfig(FeastConfigBaseModel): """Configuration for the MongoDB agg offline store (single shared collection).""" - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_agg.MongoDBOfflineStoreAgg" + type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStore" connection_string: StrictStr = "mongodb://localhost:27017" """MongoDB connection URI""" @@ -117,7 +121,7 @@ class MongoDBOfflineStoreAggConfig(FeastConfigBaseModel): # --------------------------------------------------------------------------- -class MongoDBSourceAgg(DataSource): +class MongoDBSource(DataSource): """Data source for the aggregation offline store. Identical semantics to MongoDBSourceOne: the ``name`` field is used as @@ -157,9 +161,9 @@ def validate(self, config: RepoConfig) -> None: pass @staticmethod - def from_proto(data_source: DataSourceProto) -> "MongoDBSourceAgg": + def from_proto(data_source: DataSourceProto) -> "MongoDBSource": assert data_source.HasField("custom_options") - return MongoDBSourceAgg( + return MongoDBSource( name=data_source.name, timestamp_field=data_source.timestamp_field, created_timestamp_column=data_source.created_timestamp_column, @@ -175,7 +179,7 @@ def _to_proto_impl(self) -> DataSourceProto: return DataSourceProto( name=self.name, type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_agg.MongoDBSourceAgg", + data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBSource", field_mapping=self.field_mapping, custom_options=DataSourceProto.CustomSourceOptions( configuration=json.dumps({"feature_view": self.name}).encode() @@ -200,7 +204,7 @@ def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: # --------------------------------------------------------------------------- -class MongoDBAggRetrievalJob(RetrievalJob): +class MongoDBRetrievalJob(RetrievalJob): def __init__( self, query_fn: Callable[[], pyarrow.Table], @@ -217,6 +221,10 @@ def __init__( def full_feature_names(self) -> bool: return self._full_feature_names + @property + def on_demand_feature_views(self) -> List[Any]: + return [] + @property def metadata(self) -> Optional[RetrievalMetadata]: return self._metadata @@ -230,15 +238,23 @@ def persist( allow_overwrite: bool = False, timeout: Optional[int] = None, ) -> None: - if isinstance(storage, SavedDatasetStorage): - if hasattr(storage, "path"): - path = storage.path - if not allow_overwrite: - import os + import os - if os.path.exists(path): - raise SavedDatasetLocationAlreadyExists(location=path) - self.to_df().to_parquet(path) + from feast.infra.offline_stores.file_source import SavedDatasetFileStorage + + if isinstance(storage, SavedDatasetFileStorage): + path = storage.file_options.uri + elif hasattr(storage, "path"): + path = storage.path # type: ignore[union-attr] + else: + raise ValueError( + f"MongoDBRetrievalJob.persist does not support " + f"{type(storage).__name__!r}. Use SavedDatasetFileStorage." + ) + + if not allow_overwrite and os.path.exists(path): + raise SavedDatasetLocationAlreadyExists(location=path) + self.to_df().to_parquet(path) # --------------------------------------------------------------------------- @@ -265,9 +281,11 @@ def _serialize_entity_key_from_row( entity_key.join_keys.append(jk) proto_val = ValueProto() vtype = join_key_types.get(jk, ValueType.UNKNOWN) - if vtype in (ValueType.INT32, ValueType.INT64) or isinstance(val, int): + if vtype == ValueType.INT32: + proto_val.int32_val = int(val) + elif vtype == ValueType.INT64 or isinstance(val, int): proto_val.int64_val = int(val) - elif vtype in (ValueType.STRING,) or isinstance(val, str): + elif vtype == ValueType.STRING or isinstance(val, str): proto_val.string_val = str(val) elif isinstance(val, float): proto_val.double_val = float(val) @@ -282,7 +300,7 @@ def _serialize_entity_key_from_row( # --------------------------------------------------------------------------- -class MongoDBOfflineStoreAgg(OfflineStore): +class MongoDBOfflineStore(OfflineStore): """MongoDB offline store using a single collection and grouped aggregation. Improves on MongoDBOfflineStoreOne by: @@ -316,7 +334,7 @@ def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: cache_key = f"{conn_str}/{db_name}/{collection}" client: Any = MongoClient(conn_str) if cache_key not in _indexes_ensured: - MongoDBOfflineStoreAgg._ensure_indexes(client, db_name, collection) + MongoDBOfflineStore._ensure_indexes(client, db_name, collection) _indexes_ensured.add(cache_key) return client @@ -331,13 +349,13 @@ def pull_latest_from_table_or_query( start_date: datetime, end_date: datetime, ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSourceAgg): + if not isinstance(data_source, MongoDBSource): raise ValueError( - f"MongoDBOfflineStoreAgg expected MongoDBSourceAgg, " + f"MongoDBOfflineStore expected MongoDBSource, " f"got {type(data_source).__name__!r}." ) warnings.warn( - "MongoDB offline store (agg) is in preview. API may change without notice.", + "MongoDB offline store is in preview. API may change without notice.", RuntimeWarning, ) db_name = config.offline_store.database @@ -369,7 +387,7 @@ def pull_latest_from_table_or_query( ] def _run() -> pyarrow.Table: - client = MongoDBOfflineStoreAgg._get_client_and_ensure_indexes(config) + client = MongoDBOfflineStore._get_client_and_ensure_indexes(config) try: docs = _fetch_documents(client, db_name, collection, pipeline) if not docs: @@ -384,7 +402,7 @@ def _run() -> pyarrow.Table: finally: client.close() - return MongoDBAggRetrievalJob( + return MongoDBRetrievalJob( query_fn=_run, full_feature_names=False, config=config ) @@ -399,13 +417,13 @@ def pull_all_from_table_or_query( start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSourceAgg): + if not isinstance(data_source, MongoDBSource): raise ValueError( - f"MongoDBOfflineStoreAgg expected MongoDBSourceAgg, " + f"MongoDBOfflineStore expected MongoDBSource, " f"got {type(data_source).__name__!r}." ) warnings.warn( - "MongoDB offline store (agg) is in preview. API may change without notice.", + "MongoDB offline store is in preview. API may change without notice.", RuntimeWarning, ) db_name = config.offline_store.database @@ -427,7 +445,7 @@ def pull_all_from_table_or_query( pipeline = [{"$match": match_filter}, {"$project": project_stage}] def _run() -> pyarrow.Table: - client = MongoDBOfflineStoreAgg._get_client_and_ensure_indexes(config) + client = MongoDBOfflineStore._get_client_and_ensure_indexes(config) try: docs = _fetch_documents(client, db_name, collection, pipeline) if not docs: @@ -442,7 +460,7 @@ def _run() -> pyarrow.Table: finally: client.close() - return MongoDBAggRetrievalJob( + return MongoDBRetrievalJob( query_fn=_run, full_feature_names=False, config=config ) @@ -455,6 +473,7 @@ def get_historical_features( registry: BaseRegistry, project: str, full_feature_names: bool = False, + strict_pit: bool = True, ) -> RetrievalJob: """Fetch historical features using grouped aggregation. @@ -470,13 +489,20 @@ def get_historical_features( Training path (repeated entity IDs at different timestamps): Omits ``$group`` and uses ``merge_asof`` in Python, matching mongodb_one behaviour but still with K-collapsed queries. + + Args: + strict_pit: When True (default) features whose document timestamp + is strictly after the entity request timestamp are returned as + NULL — this is the safe training/evaluation default. Set to + False for real-time scoring where you want the most recent + observation even if it post-dates the nominal request time. """ if isinstance(entity_df, str): raise ValueError( - "MongoDBOfflineStoreAgg does not support SQL entity_df strings." + "MongoDBOfflineStore does not support SQL entity_df strings." ) warnings.warn( - "MongoDB offline store (agg) is in preview. API may change without notice.", + "MongoDB offline store is in preview. API may change without notice.", RuntimeWarning, ) @@ -507,8 +533,8 @@ def get_historical_features( for fv in feature_views } - CHUNK_SIZE = 50_000 - MONGO_BATCH_SIZE = 10_000 + CHUNK_SIZE = _CHUNK_SIZE + MONGO_BATCH_SIZE = _MONGO_BATCH_SIZE def _chunk_dataframe( df: pd.DataFrame, size: int @@ -680,9 +706,17 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: fv_join, on="_fv_entity_id", how="left" ) - # Mask: fv doc is in the future relative to entity request - # time (max_ts overshoot) or outside TTL window. - future_mask = merged["_fv_ts"] > merged[event_timestamp_col] + # Mask: fv doc is outside valid window for entity request. + # strict_pit=True (default): null out docs from the future + # relative to the entity request timestamp (max_ts overshoot). + # strict_pit=False: accept the most recent doc regardless of + # whether it post-dates the request (real-time inference). + if strict_pit: + future_mask = merged["_fv_ts"] > merged[event_timestamp_col] + else: + future_mask = pd.Series( + [False] * len(merged), index=merged.index + ) if fv and fv.ttl: ttl_mask = merged["_fv_ts"] < ( merged[event_timestamp_col] - fv.ttl @@ -760,7 +794,7 @@ def _run() -> pyarrow.Table: working_df = entity_df.copy() working_df["_row_idx"] = range(len(working_df)) - client = MongoDBOfflineStoreAgg._get_client_and_ensure_indexes(config) + client = MongoDBOfflineStore._get_client_and_ensure_indexes(config) try: coll = client[db_name][feature_collection] if len(working_df) <= CHUNK_SIZE: @@ -785,7 +819,7 @@ def _run() -> pyarrow.Table: return pyarrow.Table.from_pandas(result_df, preserve_index=False) - return MongoDBAggRetrievalJob( + return MongoDBRetrievalJob( query_fn=_run, full_feature_names=full_feature_names, config=config, @@ -817,15 +851,15 @@ def offline_write_batch( Args: config: Feast repo configuration. feature_view: The feature view being written; must have a - MongoDBSourceAgg batch source. + MongoDBSource batch source. table: Arrow table with join key columns, feature columns, ``event_timestamp``, and optionally ``created_at``. progress: Optional callback invoked with the row count after each batch insert. """ - if not isinstance(feature_view.batch_source, MongoDBSourceAgg): + if not isinstance(feature_view.batch_source, MongoDBSource): raise ValueError( - f"MongoDBOfflineStoreAgg.offline_write_batch expected a MongoDBSourceAgg " + f"MongoDBOfflineStore.offline_write_batch expected a MongoDBSource " f"batch source, got {type(feature_view.batch_source).__name__!r}." ) @@ -902,7 +936,7 @@ def offline_write_batch( } ) - client = MongoDBOfflineStoreAgg._get_client_and_ensure_indexes(config) + client = MongoDBOfflineStore._get_client_and_ensure_indexes(config) try: coll = client[db_name][collection_name] BATCH_SIZE = 10_000 diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py deleted file mode 100644 index b1112552f39..00000000000 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_many.py +++ /dev/null @@ -1,678 +0,0 @@ -# Copyright 2026 The Feast Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -MongoDB Offline Store Implementation (Many Collections). - -This module implements a MongoDB offline store using a many-collection schema -where each FeatureView maps to its own dedicated MongoDB collection. It uses -Ibis for point-in-time joins, loading collection data into in-memory tables. - -Collection Structure: - Each FeatureView has its own collection named after the source: - - driver_stats FeatureView → db.driver_stats collection - - vehicle_stats FeatureView → db.vehicle_stats collection - -Collection Index (auto-created during materialization): - db..createIndex({ - "": 1, - "": 1, // if compound key - "event_timestamp": -1, - "created_at": -1 // if created_timestamp_column is set - }) - -Document Schema (example for driver_stats): - { - "_id": ObjectId(), - "driver_id": 1001, - "event_timestamp": ISODate("2026-01-20T12:00:00Z"), - "created_at": ISODate("2026-01-20T12:00:05Z"), - "rating": 4.91, - "trips_last_7d": 132 - } - - Note: Features are stored as top-level fields (flat schema), not nested - in a subdocument. This differs from the "One" implementation. - -Feature Freshness Semantics: - This implementation operates at *document-level freshness*, not - per-feature freshness. During retrieval (e.g. point-in-time joins), - the system selects the most recent document for a given entity that - satisfies time constraints, and then extracts all requested features - from that document. - - As a result, if a newer document contains only a subset of features, - missing features will be returned as NULL—even if older documents - contained values for those features. The system does not backfill - individual feature values from earlier events. - - This behavior matches common Feast offline store semantics, but may - differ from systems that compute "latest value per feature". - -Schema Evolution ("Feature Creep"): - Because documents can have varying fields over time, different documents - in the same collection may contain different sets of feature fields. - This supports: - - Adding new features without backfilling historical data - - Partial writes or sparse feature computation - - However, it also implies: - - Newly added features will be NULL for older events - - Partially populated documents may lead to NULL values even - when older data contained those features - - Users should ensure that feature computation pipelines write complete - feature sets when consistent availability is required. - -Notes: - - Entity keys are stored as native MongoDB types (not serialized), - which differs from the "One" implementation. - - Point-in-time correctness is enforced per FeatureView. - - TTL (time-to-live) constraints are applied per FeatureView during - historical retrieval. - -Point-in-Time Join Strategy: - 1. Load entire collection into an Ibis memtable - 2. Load entity_df into an Ibis memtable - 3. Use Ibis/pandas merge_asof for point-in-time correctness - 4. Apply TTL filtering per FeatureView - -Performance Characteristics: - - Fast for small to medium collections (fits in memory) - - Optimized Ibis memtable operations for joins - - ⚠️ Loads ENTIRE collection into memory - may OOM on large collections - -When to Use: - ✅ Small to medium feature stores where collections fit in memory - ✅ When query performance is the priority - ✅ When you want simple, flat document schemas - ✅ When each FeatureView has independent scaling needs - - ❌ Avoid when collections are very large (use MongoDBOfflineStoreOne instead) - ❌ Avoid in memory-constrained environments - -Comparison with MongoDBOfflineStoreOne: - | Aspect | Many (this module) | One | - |-----------------|----------------------|------------------------| - | Collections | N (one per FV) | 1 (shared) | - | Schema | Flat top-level | Nested features{} | - | Memory | Loads all docs | Filters by entity | - | Performance | Faster at scale | Memory-efficient | - | Entity ID | Native columns | Serialized bytes | -""" - -import json -import warnings -from datetime import datetime -from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union, cast - -import ibis -import pandas as pd -from ibis.expr.types import Table -from pydantic import StrictStr - -try: - from pymongo import MongoClient -except ImportError: - MongoClient = None # type: ignore[assignment,misc] - -from feast.data_source import DataSource -from feast.errors import ( - DataSourceNoNameException, - FeastExtrasDependencyImportError, - SavedDatasetLocationAlreadyExists, -) -from feast.feature_view import FeatureView -from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA -from feast.infra.offline_stores.ibis import ( - get_historical_features_ibis, - pull_all_from_table_or_query_ibis, - pull_latest_from_table_or_query_ibis, -) -from feast.infra.offline_stores.offline_store import ( - OfflineStore, - RetrievalJob, -) -from feast.infra.registry.base_registry import BaseRegistry -from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto -from feast.protos.feast.core.SavedDataset_pb2 import ( - SavedDatasetStorage as SavedDatasetStorageProto, -) -from feast.repo_config import FeastConfigBaseModel, RepoConfig -from feast.saved_dataset import SavedDatasetStorage -from feast.type_map import mongodb_to_feast_value_type -from feast.value_type import ValueType - -# --------------------------------------------------------------------------- -# Helper functions -# --------------------------------------------------------------------------- - - -def _infer_python_type_str(value: Any) -> Optional[str]: - """Infer a Feast-compatible type string from a Python value returned by pymongo.""" - if value is None: - return None - if isinstance(value, bool): - return "bool" - if isinstance(value, int): - return "int" - if isinstance(value, float): - return "float" - if isinstance(value, str): - return "str" - if isinstance(value, bytes): - return "bytes" - if isinstance(value, datetime): - return "datetime" - if isinstance(value, list): - if not value: - return "list[str]" - elem_type = _infer_python_type_str(value[0]) - if elem_type: - return f"list[{elem_type}]" - return "list[str]" - return None - - -# --------------------------------------------------------------------------- -# MongoDBSourceMany and related classes (one collection per FeatureView) -# --------------------------------------------------------------------------- - - -class MongoDBOptionsMany: - """Options for a MongoDB data source (database + collection).""" - - def __init__(self, database: str, collection: str): - self._database = database - self._collection = collection - - def to_proto(self) -> DataSourceProto.CustomSourceOptions: - """Serialize database and collection names as JSON into a CustomSourceOptions proto.""" - return DataSourceProto.CustomSourceOptions( - configuration=json.dumps( - {"database": self._database, "collection": self._collection} - ).encode() - ) - - @classmethod - def from_proto( - cls, options_proto: DataSourceProto.CustomSourceOptions - ) -> "MongoDBOptionsMany": - """Deserialize a CustomSourceOptions proto back into a MongoDBOptionsMany instance.""" - config = json.loads(options_proto.configuration.decode("utf8")) - return cls(database=config["database"], collection=config["collection"]) - - -class MongoDBSourceMany(DataSource): - """A MongoDB collection used as a Feast offline data source (one collection per FeatureView). - - ``name`` is the logical Feast name for this source. If omitted, it defaults - to the value of ``collection``. At least one of ``name`` or ``collection`` - must be supplied. - - ``database`` is the MongoDB database that contains the collection. When - omitted it falls back to ``MongoDBOfflineStoreManyConfig.database`` at query - time, so a single store-level default can be shared across many sources. - - ``schema_sample_size`` controls how many documents are randomly sampled - when Feast infers the collection schema (used by ``feast apply`` and - ``get_table_column_names_and_types``). Increase it for collections with - highly variable document shapes; decrease it to speed up ``feast apply`` - at the cost of schema coverage. - """ - - def source_type(self) -> DataSourceProto.SourceType.ValueType: - return DataSourceProto.CUSTOM_SOURCE - - def __init__( - self, - name: Optional[str] = None, - database: Optional[str] = None, - collection: Optional[str] = None, - timestamp_field: Optional[str] = "", - created_timestamp_column: Optional[str] = "", - field_mapping: Optional[Dict[str, str]] = None, - description: Optional[str] = "", - tags: Optional[Dict[str, str]] = None, - owner: Optional[str] = "", - schema_sample_size: int = 100, - ): - if name is None and collection is None: - raise DataSourceNoNameException() - # At least one of name / collection is non-None; cast to satisfy the type checker. - name = cast(str, name or collection) - - self._mongodb_options = MongoDBOptionsMany( - database=database or "", - collection=collection or name, - ) - self._schema_sample_size = schema_sample_size - - super().__init__( - name=name, - timestamp_field=timestamp_field, - created_timestamp_column=created_timestamp_column, - field_mapping=field_mapping, - description=description, - tags=tags, - owner=owner, - ) - - def __hash__(self): - return super().__hash__() - - def __eq__(self, other): - if not isinstance(other, MongoDBSourceMany): - raise TypeError( - "Comparisons should only involve MongoDBSourceMany class objects." - ) - return ( - super().__eq__(other) - and self._mongodb_options._database == other._mongodb_options._database - and self._mongodb_options._collection == other._mongodb_options._collection - and self.timestamp_field == other.timestamp_field - and self.created_timestamp_column == other.created_timestamp_column - and self.field_mapping == other.field_mapping - ) - - @property - def database(self) -> str: - return self._mongodb_options._database - - @property - def collection(self) -> str: - return self._mongodb_options._collection - - @staticmethod - def from_proto(data_source: DataSourceProto) -> "MongoDBSourceMany": - assert data_source.HasField("custom_options") - options = json.loads(data_source.custom_options.configuration) - return MongoDBSourceMany( - name=data_source.name, - database=options["database"], - collection=options["collection"], - field_mapping=dict(data_source.field_mapping), - timestamp_field=data_source.timestamp_field, - created_timestamp_column=data_source.created_timestamp_column, - description=data_source.description, - tags=dict(data_source.tags), - owner=data_source.owner, - ) - - def _to_proto_impl(self) -> DataSourceProto: - data_source_proto = DataSourceProto( - name=self.name, - type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many.MongoDBSourceMany", - field_mapping=self.field_mapping, - custom_options=self._mongodb_options.to_proto(), - description=self.description, - tags=self.tags, - owner=self.owner, - ) - data_source_proto.timestamp_field = self.timestamp_field - data_source_proto.created_timestamp_column = self.created_timestamp_column - return data_source_proto - - def validate(self, config: RepoConfig): - # No upfront schema validation is required for MongoDB; the connection - # is exercised lazily when features are actually retrieved. - pass - - @staticmethod - def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: - return mongodb_to_feast_value_type - - def get_table_query_string(self) -> str: - return f"{self._mongodb_options._database}.{self._mongodb_options._collection}" - - def get_table_column_names_and_types( - self, config: RepoConfig - ) -> Iterable[Tuple[str, str]]: - """Sample documents from the collection to infer field names and their Feast type strings. - - Uses ``$sample`` to fetch up to ``schema_sample_size`` documents, then - picks the most-frequent Python type observed per field. The ``_id`` - field is always excluded. - """ - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - connection_string = config.offline_store.connection_string - db_name = self.database or config.offline_store.database - client: Any = MongoClient(connection_string, tz_aware=True) - try: - docs = list( - client[db_name][self.collection].aggregate( - [{"$sample": {"size": self._schema_sample_size}}] - ) - ) - finally: - client.close() - - field_type_counts: Dict[str, Dict[str, int]] = {} - for doc in docs: - for field, value in doc.items(): - if field == "_id": - continue - type_str = _infer_python_type_str(value) - if type_str is None: - continue - field_type_counts.setdefault(field, {}) - field_type_counts[field][type_str] = ( - field_type_counts[field].get(type_str, 0) + 1 - ) - - return [ - (field, max(counts, key=lambda t: counts[t])) - for field, counts in field_type_counts.items() - ] - - -class SavedDatasetMongoDBStorageMany(SavedDatasetStorage): - """Persists a Feast SavedDataset into a MongoDB collection (many-collection schema).""" - - _proto_attr_name = "custom_storage" - - mongodb_options: MongoDBOptionsMany - - def __init__(self, database: str, collection: str): - self.mongodb_options = MongoDBOptionsMany( - database=database, - collection=collection, - ) - - @staticmethod - def from_proto( - storage_proto: SavedDatasetStorageProto, - ) -> "SavedDatasetMongoDBStorageMany": - options = json.loads(storage_proto.custom_storage.configuration) - return SavedDatasetMongoDBStorageMany( - database=options["database"], - collection=options["collection"], - ) - - def to_proto(self) -> SavedDatasetStorageProto: - return SavedDatasetStorageProto(custom_storage=self.mongodb_options.to_proto()) - - def to_data_source(self) -> DataSource: - return MongoDBSourceMany( - database=self.mongodb_options._database, - collection=self.mongodb_options._collection, - ) - - -# --------------------------------------------------------------------------- -# Offline store configuration and implementation -# --------------------------------------------------------------------------- - - -class MongoDBOfflineStoreManyConfig(FeastConfigBaseModel): - """Configuration for the MongoDB offline store (one collection per FeatureView).""" - - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many.MongoDBOfflineStoreMany" - """Offline store type selector""" - - connection_string: StrictStr = "mongodb://localhost:27017" - """MongoDB connection URI""" - - database: StrictStr = "feast" - """Default MongoDB database name""" - - -class MongoDBOfflineStoreMany(OfflineStore): - """Offline store backed by MongoDB (one collection per FeatureView). - - Uses Ibis memtables for point-in-time joins. Each FeatureView's data is stored - in a separate MongoDB collection, with the collection name matching the source name. - """ - - @staticmethod - def pull_latest_from_table_or_query( - config: RepoConfig, - data_source: DataSource, - join_key_columns: List[str], - feature_name_columns: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str], - start_date: datetime, - end_date: datetime, - ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSourceMany): - raise ValueError( - f"MongoDBOfflineStoreMany expected a MongoDBSourceMany, " - f"got {type(data_source).__name__!r}." - ) - warnings.warn( - "MongoDB offline store is in preview. API may change without notice.", - RuntimeWarning, - ) - - # Ensure index exists for efficient queries - if MongoClient is not None: - connection_string = config.offline_store.connection_string - db_name = data_source.database or config.offline_store.database - client: Any = MongoClient( - connection_string, driver=DRIVER_METADATA, tz_aware=True - ) - try: - _ensure_index_many( - client=client, - db_name=db_name, - collection_name=data_source.collection, - join_keys=join_key_columns, - timestamp_field=timestamp_field, - created_timestamp_column=created_timestamp_column, - ) - finally: - client.close() - - return pull_latest_from_table_or_query_ibis( - config=config, - data_source=data_source, - join_key_columns=join_key_columns, - feature_name_columns=feature_name_columns, - timestamp_field=timestamp_field, - created_timestamp_column=created_timestamp_column, - start_date=start_date, - end_date=end_date, - data_source_reader=_build_data_source_reader(config), - data_source_writer=_build_data_source_writer(config), # type: ignore[arg-type] - ) - - @staticmethod - def get_historical_features( - config: RepoConfig, - feature_views: List[FeatureView], - feature_refs: List[str], - entity_df: Union[pd.DataFrame, str], - registry: BaseRegistry, - project: str, - full_feature_names: bool = False, - ) -> RetrievalJob: - warnings.warn( - "MongoDB offline store is in preview. API may change without notice.", - RuntimeWarning, - ) - return get_historical_features_ibis( - config=config, - feature_views=feature_views, - feature_refs=feature_refs, - entity_df=entity_df, - registry=registry, - project=project, - full_feature_names=full_feature_names, - data_source_reader=_build_data_source_reader(config), - data_source_writer=_build_data_source_writer(config), # type: ignore[arg-type] - ) - - @staticmethod - def pull_all_from_table_or_query( - config: RepoConfig, - data_source: DataSource, - join_key_columns: List[str], - feature_name_columns: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str] = None, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSourceMany): - raise ValueError( - f"MongoDBOfflineStoreMany expected a MongoDBSourceMany, " - f"got {type(data_source).__name__!r}." - ) - warnings.warn( - "MongoDB offline store is in preview. API may change without notice.", - RuntimeWarning, - ) - return pull_all_from_table_or_query_ibis( - config=config, - data_source=data_source, - join_key_columns=join_key_columns, - feature_name_columns=feature_name_columns, - timestamp_field=timestamp_field, - created_timestamp_column=created_timestamp_column, - start_date=start_date, - end_date=end_date, - data_source_reader=_build_data_source_reader(config), - data_source_writer=_build_data_source_writer(config), # type: ignore[arg-type] - ) - - -def _build_data_source_reader(config: RepoConfig) -> Callable[[DataSource, str], Table]: - """Return a closure that fetches a MongoDB collection as an ibis in-memory table.""" - - def reader(data_source: DataSource, repo_path: str) -> Table: - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - if not isinstance(data_source, MongoDBSourceMany): - raise ValueError( - f"MongoDBOfflineStoreMany reader expected a MongoDBSourceMany, " - f"got {type(data_source).__name__!r}." - ) - connection_string = config.offline_store.connection_string - db_name = data_source.database or config.offline_store.database - client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) - try: - docs = list(client[db_name][data_source.collection].find({}, {"_id": 0})) - finally: - client.close() - - df = pd.DataFrame(docs) - if df.empty: - return ibis.memtable(df) - - # Localize naive datetime columns to UTC. MongoDB stores all dates as UTC, - # and with tz_aware=False (default), pymongo returns naive datetime objects. - # We convert them to timezone-aware UTC timestamps for pyarrow compatibility. - for col in df.columns: - if df[col].dtype == object and len(df[col].dropna()) > 0: - sample = df[col].dropna().iloc[0] - if isinstance(sample, datetime): - try: - df[col] = pd.to_datetime(df[col], utc=True) - except (ValueError, TypeError): - # Skip columns that can't be converted (e.g., mixed types) - pass - - return ibis.memtable(df) - - return reader - - -# Track which collections have had indexes ensured (module-level cache) -_indexes_ensured: set = set() - - -def _ensure_index_many( - client: Any, - db_name: str, - collection_name: str, - join_keys: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str] = None, -) -> None: - """Create recommended index on a Many-schema collection. - - Index is on: join_keys (ascending) + timestamp (descending) + created_at (descending). - Uses a module-level cache to avoid redundant index creation checks. - """ - cache_key = f"{db_name}.{collection_name}" - if cache_key in _indexes_ensured: - return - - coll = client[db_name][collection_name] - - # Build index key: join_keys (asc) + timestamp (desc) + created_at (desc) - index_keys = [(k, 1) for k in join_keys] - index_keys.append((timestamp_field, -1)) - if created_timestamp_column: - index_keys.append((created_timestamp_column, -1)) - - # Check if equivalent index already exists - existing_indexes = coll.index_information() - for idx_info in existing_indexes.values(): - if idx_info.get("key") == index_keys: - _indexes_ensured.add(cache_key) - return - - # Create the index - coll.create_index(index_keys, background=True) - _indexes_ensured.add(cache_key) - - -def _build_data_source_writer( - config: RepoConfig, -) -> Callable[[Table, DataSource, str, str, bool], None]: - """Return a closure that writes an ibis table to a MongoDB collection.""" - - def writer( - table: Table, - data_source: DataSource, - repo_path: str, - mode: str = "append", - allow_overwrite: bool = False, - ) -> None: - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - if not isinstance(data_source, MongoDBSourceMany): - raise ValueError( - f"MongoDBOfflineStoreMany writer expected a MongoDBSourceMany, " - f"got {type(data_source).__name__!r}." - ) - connection_string = config.offline_store.connection_string - db_name = data_source.database or config.offline_store.database - location = f"{db_name}.{data_source.collection}" - client: Any = MongoClient( - connection_string, driver=DRIVER_METADATA, tz_aware=True - ) - try: - coll = client[db_name][data_source.collection] - if mode == "overwrite": - if not allow_overwrite and coll.estimated_document_count() > 0: - raise SavedDatasetLocationAlreadyExists(location=location) - coll.drop() - records = table.to_pyarrow().to_pylist() - if records: - coll.insert_many(records) - finally: - client.close() - - return writer diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py deleted file mode 100644 index 10ce735fceb..00000000000 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_native.py +++ /dev/null @@ -1,1110 +0,0 @@ -# Copyright 2026 The Feast Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Native MongoDB Offline Store — Atlas-first, pure-MQL implementation. - -All feature views share a single ``feature_history`` collection, discriminated -by a ``feature_view`` field. Historical retrieval (point-in-time join) is -executed entirely as a MongoDB aggregation pipeline using ``$documents`` + -``$lookup``, so all sorting, grouping, and TTL filtering run on the Atlas -cluster rather than on the client. - -Minimum server version: MongoDB 5.1 (Atlas M10+ or self-managed 5.1+). -``$documents`` (the stage that injects the entity DataFrame into the pipeline -without a temp collection) was introduced in 5.1. - -Collection Index (compound, covers both PIT join and pull-latest): - - db.feature_history.create_index([ - ("entity_id", ASCENDING), - ("feature_view", ASCENDING), - ("event_timestamp", DESCENDING), - ("created_at", DESCENDING), - ]) - - The ``created_at`` key is required so that data corrections (two documents - with the same entity_id + feature_view + event_timestamp) are resolved - deterministically: the document with the later created_at wins. - -Document Schema: - - { - "_id": ObjectId(), - "entity_id": Binary(...), # serialized entity key - "feature_view": "driver_stats", - "features": { - "conv_rate": 0.72, - "trips_last_7d": 132 - }, - "event_timestamp": ISODate("2026-01-20T12:00:00Z"), - "created_at": ISODate("2026-01-20T12:00:05Z") - } - -PIT Join Strategy (get_historical_features): - - For each chunk of the entity DataFrame: - - 1. Python serializes one entity key per (row, feature_view), using only - that feature view's declared join keys and value types. This is the - key correctness invariant: driver_stats is keyed by {driver_id} and - customer_stats by {customer_id}; using the union would produce bytes - that never match stored documents. - - 2. A single ``$documents`` stage injects the chunk as a virtual collection: - each document carries ``_row_idx``, ``_ts``, and a per-FV - ``_entity_ids`` subdocument. - - 3. One ``$lookup`` stage per feature view performs the correlated PIT join - entirely on Atlas: - - match: entity_id == $$eid, feature_view == , - event_timestamp <= $$ts, (optional) event_timestamp >= $$ts - ttl - - sort: event_timestamp DESC, created_at DESC - - limit: 1 - - 4. A final ``$project`` drops the entity-id payload before results are - sent over the network. - - 5. Python assembles the flat result DataFrame from the matched docs. - -Feature Freshness Semantics: - - This implementation uses document-level freshness: the most recent document - for (entity_id, feature_view) whose event_timestamp <= entity row's - timestamp is selected, and all requested features are read from that one - document. If a newer document is missing a feature that an older document - had, the value is NULL. This matches standard Feast offline store semantics. -""" - -import json -import warnings -from collections import defaultdict -from datetime import datetime, timezone -from typing import ( - Any, - Callable, - Dict, - Iterable, - List, - Optional, - Tuple, - Union, -) - -import pandas as pd -import pyarrow - -try: - from pymongo import MongoClient -except ImportError: - MongoClient = None # type: ignore[assignment,misc] - -from pydantic import StrictStr - -from feast.data_source import DataSource -from feast.errors import ( - DataSourceNoNameException, - FeastExtrasDependencyImportError, - SavedDatasetLocationAlreadyExists, -) -from feast.feature_view import FeatureView -from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA -from feast.infra.offline_stores.offline_store import ( - OfflineStore, - RetrievalJob, - RetrievalMetadata, -) -from feast.infra.offline_stores.offline_utils import ( - get_expected_join_keys, - infer_event_timestamp_from_entity_df, -) -from feast.infra.registry.base_registry import BaseRegistry -from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto -from feast.protos.feast.core.SavedDataset_pb2 import ( - SavedDatasetStorage as SavedDatasetStorageProto, -) -from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto -from feast.protos.feast.types.Value_pb2 import Value as ValueProto -from feast.repo_config import FeastConfigBaseModel, RepoConfig -from feast.saved_dataset import SavedDatasetStorage -from feast.type_map import mongodb_to_feast_value_type -from feast.value_type import ValueType - -# --------------------------------------------------------------------------- -# Index tracking — keyed by (connection_string, database, collection) so that -# multiple RepoConfigs pointing at different clusters never share state. -# --------------------------------------------------------------------------- -_indexes_ensured: set = set() - -# Chunk size for entity_df processing. Each chunk becomes one $documents -# stage; keep it small enough that the aggregation command stays well under -# MongoDB's 16 MB BSON document limit. -_CHUNK_SIZE = 50_000 - - -# --------------------------------------------------------------------------- -# Configuration -# --------------------------------------------------------------------------- - - -class MongoDBOfflineStoreNativeConfig(FeastConfigBaseModel): - """Configuration for the native MongoDB offline store (Atlas-first).""" - - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native.MongoDBOfflineStoreNative" - - connection_string: StrictStr = "mongodb://localhost:27017" - """MongoDB connection URI""" - - database: StrictStr = "feast" - """MongoDB database name""" - - collection: StrictStr = "feature_history" - """Single collection name shared by all feature views""" - - -# --------------------------------------------------------------------------- -# DataSource -# --------------------------------------------------------------------------- - - -class MongoDBSourceNative(DataSource): - """MongoDB data source for the native (Atlas-first) offline store. - - All feature views share a single collection. The ``name`` of this source - doubles as the ``feature_view`` discriminator value stored in each document. - """ - - def __init__( - self, - name: Optional[str] = None, - timestamp_field: str = "event_timestamp", - created_timestamp_column: str = "created_at", - field_mapping: Optional[Dict[str, str]] = None, - description: Optional[str] = "", - tags: Optional[Dict[str, str]] = None, - owner: Optional[str] = "", - ): - if name is None: - raise DataSourceNoNameException() - super().__init__( - name=name, - timestamp_field=timestamp_field, - created_timestamp_column=created_timestamp_column, - field_mapping=field_mapping, - description=description, - tags=tags, - owner=owner, - ) - - def __hash__(self): - return super().__hash__() - - def __eq__(self, other): - if not isinstance(other, MongoDBSourceNative): - raise TypeError( - "Comparisons should only involve MongoDBSourceNative class objects." - ) - return ( - super().__eq__(other) - and self.timestamp_field == other.timestamp_field - and self.created_timestamp_column == other.created_timestamp_column - and self.field_mapping == other.field_mapping - ) - - @property - def feature_view_name(self) -> str: - """The feature_view discriminator value stored in each document.""" - return self.name - - def source_type(self) -> DataSourceProto.SourceType.ValueType: - return DataSourceProto.CUSTOM_SOURCE - - @staticmethod - def from_proto(data_source: DataSourceProto) -> "MongoDBSourceNative": - assert data_source.HasField("custom_options") - return MongoDBSourceNative( - name=data_source.name, - timestamp_field=data_source.timestamp_field, - created_timestamp_column=data_source.created_timestamp_column, - field_mapping=dict(data_source.field_mapping), - description=data_source.description, - tags=dict(data_source.tags), - owner=data_source.owner, - ) - - def _to_proto_impl(self) -> DataSourceProto: - return DataSourceProto( - name=self.name, - type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type=( - "feast.infra.offline_stores.contrib.mongodb_offline_store" - ".mongodb_native.MongoDBSourceNative" - ), - field_mapping=self.field_mapping, - custom_options=DataSourceProto.CustomSourceOptions( - configuration=json.dumps({"feature_view": self.name}).encode() - ), - description=self.description, - tags=self.tags, - owner=self.owner, - timestamp_field=self.timestamp_field, - created_timestamp_column=self.created_timestamp_column, - ) - - def validate(self, config: RepoConfig): - pass - - @staticmethod - def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: - return mongodb_to_feast_value_type - - def get_table_query_string(self) -> str: - return f"feature_history[feature_view={self.name}]" - - def get_table_column_names_and_types( - self, config: RepoConfig - ) -> Iterable[Tuple[str, str]]: - """Sample documents to infer feature names and types.""" - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - client: Any = MongoClient( - config.offline_store.connection_string, driver=DRIVER_METADATA - ) - try: - pipeline = [ - {"$match": {"feature_view": self.name}}, - {"$sample": {"size": 100}}, - ] - docs = list( - client[config.offline_store.database][ - config.offline_store.collection - ].aggregate(pipeline) - ) - finally: - client.close() - - field_type_counts: Dict[str, Dict[str, int]] = {} - for doc in docs: - for field, value in doc.get("features", {}).items(): - type_str = _infer_python_type_str(value) - if type_str is None: - continue - field_type_counts.setdefault(field, {}) - field_type_counts[field][type_str] = ( - field_type_counts[field].get(type_str, 0) + 1 - ) - - return [ - (field, max(counts, key=lambda t: counts[t])) - for field, counts in field_type_counts.items() - ] - - -# --------------------------------------------------------------------------- -# SavedDataset storage -# --------------------------------------------------------------------------- - - -class SavedDatasetMongoDBStorageNative(SavedDatasetStorage): - """Persists a SavedDataset as a flat MongoDB collection (native store).""" - - _proto_attr_name = "custom_storage" - - def __init__(self, database: str, collection: str): - self._database = database - self._collection = collection - - @staticmethod - def from_proto( - storage_proto: SavedDatasetStorageProto, - ) -> "SavedDatasetMongoDBStorageNative": - config = json.loads(storage_proto.custom_storage.configuration) - return SavedDatasetMongoDBStorageNative( - database=config["database"], - collection=config["collection"], - ) - - def to_proto(self) -> SavedDatasetStorageProto: - return SavedDatasetStorageProto( - custom_storage=DataSourceProto.CustomSourceOptions( - configuration=json.dumps( - {"database": self._database, "collection": self._collection} - ).encode() - ) - ) - - def to_data_source(self) -> DataSource: - return MongoDBSourceNative(name=self._collection) - - -# --------------------------------------------------------------------------- -# RetrievalJob -# --------------------------------------------------------------------------- - - -class MongoDBNativeRetrievalJob(RetrievalJob): - """Retrieval job for the native MongoDB offline store.""" - - def __init__( - self, - query_fn: Callable[[], pyarrow.Table], - full_feature_names: bool, - config: RepoConfig, - on_demand_feature_views: Optional[List[Any]] = None, - metadata: Optional[RetrievalMetadata] = None, - ): - self._query_fn = query_fn - self._full_feature_names = full_feature_names - self._config = config - self._on_demand_feature_views = on_demand_feature_views or [] - self._metadata = metadata - - @property - def full_feature_names(self) -> bool: - return self._full_feature_names - - @property - def on_demand_feature_views(self) -> List[Any]: - return self._on_demand_feature_views - - def _to_df_internal(self, timeout: Optional[int] = None) -> pd.DataFrame: - return self._to_arrow_internal(timeout).to_pandas() - - def _to_arrow_internal(self, timeout: Optional[int] = None) -> pyarrow.Table: - return self._query_fn() - - @property - def metadata(self) -> Optional[RetrievalMetadata]: - return self._metadata - - def persist( - self, - storage: SavedDatasetStorage, - allow_overwrite: bool = False, - timeout: Optional[int] = None, - ) -> None: - if not isinstance(storage, SavedDatasetMongoDBStorageNative): - raise ValueError( - f"MongoDBNativeRetrievalJob.persist expected " - f"SavedDatasetMongoDBStorageNative, got " - f"{type(storage).__name__!r}." - ) - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - db_name = storage._database or self._config.offline_store.database - coll_name = storage._collection - location = f"{db_name}.{coll_name}" - - client: Any = MongoClient( - self._config.offline_store.connection_string, - driver=DRIVER_METADATA, - tz_aware=True, - ) - try: - coll = client[db_name][coll_name] - if not allow_overwrite and coll.estimated_document_count() > 0: - raise SavedDatasetLocationAlreadyExists(location=location) - if allow_overwrite: - coll.drop() - records = self._to_arrow_internal().to_pylist() - if records: - coll.insert_many(records) - finally: - client.close() - - -# --------------------------------------------------------------------------- -# Module-level helpers -# --------------------------------------------------------------------------- - - -def _infer_python_type_str(value: Any) -> Optional[str]: - """Infer a Feast-compatible type string from a Python value.""" - if value is None: - return None - if isinstance(value, bool): - return "bool" - if isinstance(value, int): - return "int" - if isinstance(value, float): - return "float" - if isinstance(value, str): - return "str" - if isinstance(value, bytes): - return "bytes" - if isinstance(value, datetime): - return "datetime" - if isinstance(value, list): - if not value: - return "list[str]" - elem_type = _infer_python_type_str(value[0]) - return f"list[{elem_type}]" if elem_type else "list[str]" - return None - - -def _serialize_entity_key_from_row( - row: pd.Series, - join_keys: List[str], - entity_key_serialization_version: int, - join_key_types: Optional[Dict[str, "ValueType"]] = None, -) -> bytes: - """Serialize an entity key from a DataFrame row. - - Args: - row: DataFrame row containing join key values. - join_keys: Names of the join key columns for this feature view. - entity_key_serialization_version: Version passed to serialize_entity_key. - join_key_types: Declared ValueType per join key, derived from - ``FeatureView.entity_columns``. Required for correct INT32 vs - INT64 serialization. Without it, all Python ``int`` values map - to ``int64_val``, silently mismatching stored INT32 entity keys. - """ - entity_key = EntityKeyProto() - for key in sorted(join_keys): - entity_key.join_keys.append(key) - value = row[key] - val = ValueProto() - if hasattr(value, "item"): - # Unwrap numpy scalars (numpy 2.0 broke isinstance(np.int64, int)) - value = value.item() - declared_type = join_key_types.get(key) if join_key_types else None - if declared_type is not None: - if declared_type == ValueType.INT32: - val.int32_val = int(value) - elif declared_type == ValueType.INT64: - val.int64_val = int(value) - elif declared_type == ValueType.STRING: - val.string_val = str(value) - elif declared_type == ValueType.BYTES: - val.bytes_val = bytes(value) - elif declared_type == ValueType.UNIX_TIMESTAMP: - val.unix_timestamp_val = int(value) - else: - if isinstance(value, bool): - val.bool_val = value - elif isinstance(value, int): - val.int64_val = value - elif isinstance(value, str): - val.string_val = value - elif isinstance(value, float): - val.double_val = value - else: - val.string_val = str(value) - entity_key.entity_values.append(val) - return serialize_entity_key(entity_key, entity_key_serialization_version) - - -# --------------------------------------------------------------------------- -# Offline store -# --------------------------------------------------------------------------- - - -class MongoDBOfflineStoreNative(OfflineStore): - """Atlas-first MongoDB offline store using a single shared collection. - - All computation — sorting, grouping, TTL filtering, and the point-in-time - join — runs as a MongoDB aggregation pipeline on the Atlas cluster. The - client serializes entity keys, constructs the pipeline, and assembles the - final flat DataFrame from the matched feature documents returned by Atlas. - - Requires MongoDB 5.1+ for the ``$documents`` aggregation stage. - """ - - @staticmethod - def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: - """Create the compound index that covers all query patterns. - - The four-key index satisfies: - - pull_latest: sort {entity_id, event_timestamp DESC, created_at DESC} - after an equality match on feature_view. - - get_historical_features: $lookup subpipeline match on entity_id - (equality) + feature_view (equality) + event_timestamp (range), - then sort by event_timestamp DESC, created_at DESC. - """ - collection = client[db_name][collection_name] - target_key = [ - ("entity_id", 1), - ("feature_view", 1), - ("event_timestamp", -1), - ("created_at", -1), - ] - existing = collection.index_information() - for idx_info in existing.values(): - if idx_info.get("key") == target_key: - return - collection.create_index( - target_key, - name="entity_fv_ts_idx", - background=True, - ) - - @staticmethod - def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: - """Return a MongoClient, creating the compound index once per process.""" - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - client: Any = MongoClient( - config.offline_store.connection_string, driver=DRIVER_METADATA - ) - cache_key = ( - config.offline_store.connection_string, - config.offline_store.database, - config.offline_store.collection, - ) - if cache_key not in _indexes_ensured: - MongoDBOfflineStoreNative._ensure_indexes( - client, - config.offline_store.database, - config.offline_store.collection, - ) - _indexes_ensured.add(cache_key) - return client - - # ------------------------------------------------------------------ - # Write path - # ------------------------------------------------------------------ - - @staticmethod - def offline_write_batch( - config: RepoConfig, - feature_view: FeatureView, - table: pyarrow.Table, - progress: Optional[Callable[[int], Any]], - ) -> None: - """Append a batch of feature observations to the feature_history collection. - - Each row in ``table`` becomes one document. Multiple documents for the - same (entity_id, feature_view, event_timestamp) are kept; corrections - win by having a later created_at. - """ - if not isinstance(feature_view.batch_source, MongoDBSourceNative): - raise ValueError( - f"MongoDBOfflineStoreNative.offline_write_batch expected a " - f"MongoDBSourceNative batch source, got " - f"{type(feature_view.batch_source).__name__!r}." - ) - - entity_key_version = config.entity_key_serialization_version - db_name = config.offline_store.database - collection_name = config.offline_store.collection - - # Derive join key names and declared types from entity_columns so that - # INT32 entities produce int32_val bytes, matching the read path. - join_key_types: Dict[str, ValueType] = { - feature_view.projection.join_key_map.get( - ec.name, ec.name - ): ec.dtype.to_value_type() - for ec in feature_view.entity_columns - } - join_keys = list(join_key_types.keys()) - - timestamp_field = feature_view.batch_source.timestamp_field - created_ts_col: Optional[str] = ( - feature_view.batch_source.created_timestamp_column or None - ) - reserved = set(join_keys) | {timestamp_field} - if created_ts_col: - reserved.add(created_ts_col) - feature_cols = [c for c in table.column_names if c not in reserved] - - df = table.to_pandas() - - for ts_col in [timestamp_field] + ([created_ts_col] if created_ts_col else []): - if ts_col in df.columns: - if not pd.api.types.is_datetime64_any_dtype(df[ts_col]): - df[ts_col] = pd.to_datetime(df[ts_col], utc=True) - elif df[ts_col].dt.tz is None: - df[ts_col] = df[ts_col].dt.tz_localize("UTC") - - df["_entity_id"] = df.apply( - lambda row: _serialize_entity_key_from_row( - row, join_keys, entity_key_version, join_key_types - ), - axis=1, - ) - - now = datetime.now(tz=timezone.utc) - docs = [] - for _, row in df.iterrows(): - features: Dict[str, Any] = {} - for col in feature_cols: - val = row[col] - if pd.isna(val): - continue - if hasattr(val, "item"): - val = val.item() - features[col] = val - - created_at = now - if created_ts_col and created_ts_col in df.columns: - ct = row[created_ts_col] - if not pd.isna(ct): - created_at = ( - ct.to_pydatetime() if hasattr(ct, "to_pydatetime") else ct - ) - - docs.append( - { - "entity_id": row["_entity_id"], - "feature_view": feature_view.name, - "features": features, - "event_timestamp": ( - row[timestamp_field].to_pydatetime() - if hasattr(row[timestamp_field], "to_pydatetime") - else row[timestamp_field] - ), - "created_at": created_at, - } - ) - - client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) - try: - coll = client[db_name][collection_name] - BATCH_SIZE = 10_000 - for i in range(0, len(docs), BATCH_SIZE): - batch = docs[i : i + BATCH_SIZE] - coll.insert_many(batch, ordered=False) - if progress: - progress(len(batch)) - finally: - client.close() - - # ------------------------------------------------------------------ - # Read path — materialization helpers - # ------------------------------------------------------------------ - - @staticmethod - def pull_latest_from_table_or_query( - config: RepoConfig, - data_source: DataSource, - join_key_columns: List[str], - feature_name_columns: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str], - start_date: datetime, - end_date: datetime, - ) -> RetrievalJob: - """Return the most recent feature document per entity within [start, end]. - - Used by the materialization engine to populate the online store. - """ - if not isinstance(data_source, MongoDBSourceNative): - raise ValueError( - f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " - f"got {type(data_source).__name__!r}." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - - db_name = config.offline_store.database - collection = config.offline_store.collection - feature_view_name = data_source.feature_view_name - start_utc = start_date.astimezone(tz=timezone.utc) - end_utc = end_date.astimezone(tz=timezone.utc) - - project_stage: Dict[str, Any] = { - "_id": 0, - "entity_id": "$doc.entity_id", - "event_timestamp": "$doc.event_timestamp", - } - if created_timestamp_column: - project_stage["created_at"] = "$doc.created_at" - for feat in feature_name_columns: - project_stage[feat] = f"$doc.features.{feat}" - - # entity_id leads the sort so the compound index backs the entire stage. - # created_at tiebreaks corrections sharing the same event_timestamp. - pipeline: List[Dict[str, Any]] = [ - { - "$match": { - "feature_view": feature_view_name, - "event_timestamp": {"$gte": start_utc, "$lte": end_utc}, - } - }, - {"$sort": {"entity_id": 1, "event_timestamp": -1, "created_at": -1}}, - {"$group": {"_id": "$entity_id", "doc": {"$first": "$$ROOT"}}}, - {"$project": project_stage}, - ] - - def _run() -> pyarrow.Table: - client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) - try: - docs = list( - client[db_name][collection].aggregate(pipeline, allowDiskUse=True) - ) - if not docs: - return pyarrow.Table.from_pydict({}) - df = pd.DataFrame(docs) - if not df.empty and "event_timestamp" in df.columns: - if df["event_timestamp"].dt.tz is None: - df["event_timestamp"] = pd.to_datetime( - df["event_timestamp"], utc=True - ) - return pyarrow.Table.from_pandas(df, preserve_index=False) - finally: - client.close() - - return MongoDBNativeRetrievalJob( - query_fn=_run, full_feature_names=False, config=config - ) - - @staticmethod - def pull_all_from_table_or_query( - config: RepoConfig, - data_source: DataSource, - join_key_columns: List[str], - feature_name_columns: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str] = None, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - ) -> RetrievalJob: - """Return all feature documents in the given time range without deduplication. - - Used for bulk export and dataset generation. - """ - if not isinstance(data_source, MongoDBSourceNative): - raise ValueError( - f"MongoDBOfflineStoreNative expected MongoDBSourceNative, " - f"got {type(data_source).__name__!r}." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - - db_name = config.offline_store.database - collection = config.offline_store.collection - feature_view_name = data_source.feature_view_name - - match_filter: Dict[str, Any] = {"feature_view": feature_view_name} - if start_date or end_date: - ts_filter: Dict[str, Any] = {} - if start_date: - ts_filter["$gte"] = start_date.astimezone(tz=timezone.utc) - if end_date: - ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) - match_filter["event_timestamp"] = ts_filter - - project_stage: Dict[str, Any] = { - "_id": 0, - "entity_id": 1, - "event_timestamp": 1, - } - if created_timestamp_column: - project_stage["created_at"] = 1 - for feat in feature_name_columns: - project_stage[feat] = f"$features.{feat}" - - pipeline: List[Dict[str, Any]] = [ - {"$match": match_filter}, - {"$project": project_stage}, - ] - - def _run() -> pyarrow.Table: - client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) - try: - docs = list( - client[db_name][collection].aggregate(pipeline, allowDiskUse=True) - ) - if not docs: - return pyarrow.Table.from_pydict({}) - df = pd.DataFrame(docs) - if not df.empty and "event_timestamp" in df.columns: - if df["event_timestamp"].dt.tz is None: - df["event_timestamp"] = pd.to_datetime( - df["event_timestamp"], utc=True - ) - return pyarrow.Table.from_pandas(df, preserve_index=False) - finally: - client.close() - - return MongoDBNativeRetrievalJob( - query_fn=_run, full_feature_names=False, config=config - ) - - # ------------------------------------------------------------------ - # Read path — point-in-time join - # ------------------------------------------------------------------ - - @staticmethod - def get_historical_features( - config: RepoConfig, - feature_views: List[FeatureView], - feature_refs: List[str], - entity_df: Union[pd.DataFrame, str], - registry: BaseRegistry, - project: str, - full_feature_names: bool = False, - ) -> RetrievalJob: - """Perform a point-in-time join entirely on the Atlas cluster. - - The entity DataFrame is injected into the aggregation pipeline via - ``$documents`` (MongoDB 5.1+). One ``$lookup`` stage per feature view - performs the correlated PIT join on the server; no temp collection is - created. Entity keys are serialized per-FV so that each feature view's - documents are matched using only that view's own join keys. - """ - if isinstance(entity_df, str): - raise ValueError( - "MongoDBOfflineStoreNative does not support SQL entity_df strings. " - "Pass a pandas DataFrame instead." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - - db_name = config.offline_store.database - feature_collection = config.offline_store.collection - entity_key_version = config.entity_key_serialization_version - - entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) - event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) - - # Map "feature_view:feature_name" refs → {fv_name: [feature, ...]} - fv_to_features: Dict[str, List[str]] = defaultdict(list) - for ref in feature_refs: - fv_name, feat_name = ref.split(":", 1) - fv_to_features[fv_name].append(feat_name) - - fv_by_name = {fv.name: fv for fv in feature_views} - - # Per-FV join keys: using only each FV's own join keys is the critical - # invariant. A driver_stats document is keyed by serialize({driver_id: - # 1001}); including customer_id in the key would produce bytes that - # never match any stored document. - fv_join_keys_by_name: Dict[str, List[str]] = { - fv.name: list(get_expected_join_keys(project, [fv], registry)) - for fv in feature_views - } - - # Declared ValueType per join key — required for INT32 vs INT64 - # correctness (see _serialize_entity_key_from_row docstring). - fv_join_key_types_by_name: Dict[str, Dict[str, ValueType]] = { - fv.name: { - fv.projection.join_key_map.get( - ec.name, ec.name - ): ec.dtype.to_value_type() - for ec in fv.entity_columns - } - for fv in feature_views - } - - # Original entity columns (everything except the timestamp). - entity_columns = [c for c in entity_df.columns if c != event_timestamp_col] - - def _run() -> pyarrow.Table: - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - - # Work on a copy with a guaranteed 0-based integer index so that - # _row_idx stored in $documents == iloc position in result. - work = entity_df.copy() - work = work.reset_index(drop=True) - - if not pd.api.types.is_datetime64_any_dtype(work[event_timestamp_col]): - work[event_timestamp_col] = pd.to_datetime( - work[event_timestamp_col], utc=True - ) - elif work[event_timestamp_col].dt.tz is None: - work[event_timestamp_col] = work[event_timestamp_col].dt.tz_localize( - "UTC" - ) - - # Serialize per-FV entity keys once for the whole DataFrame. - # Column names use a prefix unlikely to clash with feature names. - for fv_name in fv_to_features: - join_keys = fv_join_keys_by_name[fv_name] - join_key_types = fv_join_key_types_by_name[fv_name] - work[f"__eid__{fv_name}"] = work.apply( - lambda row, jk=join_keys, jkt=join_key_types: ( - _serialize_entity_key_from_row(row, jk, entity_key_version, jkt) - ), - axis=1, - ) - - client = MongoDBOfflineStoreNative._get_client_and_ensure_indexes(config) - try: - db = client[db_name] - all_output_rows: List[Dict[str, Any]] = [] - - for chunk_start in range(0, len(work), _CHUNK_SIZE): - chunk = work.iloc[chunk_start : chunk_start + _CHUNK_SIZE] - - # Build the $documents array. Each entry carries: - # _row_idx – position in work (used to recover entity cols) - # _ts – entity row's event_timestamp (PIT upper bound) - # _entity_ids – per-FV serialized key bytes - documents: List[Dict[str, Any]] = [] - for row_idx, row in chunk.iterrows(): - ts = row[event_timestamp_col] - if hasattr(ts, "to_pydatetime"): - ts = ts.to_pydatetime() - documents.append( - { - "_row_idx": int(row_idx), - "_ts": ts, - "_entity_ids": { - fv_name: row[f"__eid__{fv_name}"] - for fv_name in fv_to_features - }, - } - ) - - # Build the pipeline: $documents + one $lookup per FV. - # - # Each $lookup: - # let eid = $_entity_ids. (Binary bytes) - # ts = $_ts (datetime) - # - # subpipeline: - # $match entity_id == $$eid - # feature_view == (equality → index) - # event_timestamp <= $$ts (PIT upper bound) - # event_timestamp >= $$ts - ttl_ms (optional) - # $sort event_timestamp DESC, created_at DESC - # $limit 1 (most recent doc) - # $project features only (reduce transfer) - pipeline: List[Dict[str, Any]] = [{"$documents": documents}] - - for fv_name, _features in fv_to_features.items(): - fv = fv_by_name.get(fv_name) - match_exprs: List[Dict[str, Any]] = [ - {"$eq": ["$entity_id", "$$eid"]}, - {"$eq": ["$feature_view", fv_name]}, - {"$lte": ["$event_timestamp", "$$ts"]}, - ] - if fv and fv.ttl: - ttl_ms = int(fv.ttl.total_seconds() * 1000) - match_exprs.append( - { - "$gte": [ - "$event_timestamp", - {"$subtract": ["$$ts", ttl_ms]}, - ] - } - ) - - pipeline.append( - { - "$lookup": { - "from": feature_collection, - "let": { - # Access the per-FV key from the - # _entity_ids subdocument. - "eid": f"$_entity_ids.{fv_name}", - "ts": "$_ts", - }, - "pipeline": [ - {"$match": {"$expr": {"$and": match_exprs}}}, - { - "$sort": { - "event_timestamp": -1, - "created_at": -1, - } - }, - {"$limit": 1}, - # Return only features — drop entity_id, - # feature_view, timestamps, and _id to - # minimise data transferred from Atlas. - { - "$project": { - "_id": 0, - "features": 1, - } - }, - ], - "as": f"_match_{fv_name}", - } - } - ) - - # Drop _entity_ids before results cross the network. - pipeline.append( - { - "$project": { - "_id": 0, - "_row_idx": 1, - **{ - f"_match_{fv_name}": 1 for fv_name in fv_to_features - }, - } - } - ) - - # $documents pipelines run against the database, not a - # specific collection. - matched_docs = list(db.aggregate(pipeline, allowDiskUse=True)) - - # Assemble flat output rows from the matched documents. - for doc in matched_docs: - row_idx = doc["_row_idx"] - orig = work.iloc[row_idx] - - out: Dict[str, Any] = {} - for col in entity_columns: - val = orig[col] - if hasattr(val, "item"): - val = val.item() - out[col] = val - out[event_timestamp_col] = orig[event_timestamp_col] - out["_row_idx"] = row_idx - - for fv_name, feats in fv_to_features.items(): - matches = doc.get(f"_match_{fv_name}", []) - feat_doc = matches[0] if matches else None - feat_values = ( - feat_doc.get("features", {}) if feat_doc else {} - ) - for feat in feats: - col_name = ( - f"{fv_name}__{feat}" if full_feature_names else feat - ) - out[col_name] = feat_values.get(feat) - - all_output_rows.append(out) - - finally: - client.close() - - # Restore original entity_df ordering. - all_output_rows.sort(key=lambda r: r["_row_idx"]) - result_df = pd.DataFrame(all_output_rows) - result_df = result_df.drop(columns=["_row_idx"], errors="ignore") - - if not result_df.empty and event_timestamp_col in result_df.columns: - if result_df[event_timestamp_col].dt.tz is None: - result_df[event_timestamp_col] = pd.to_datetime( - result_df[event_timestamp_col], utc=True - ) - - return pyarrow.Table.from_pandas(result_df, preserve_index=False) - - return MongoDBNativeRetrievalJob( - query_fn=_run, - full_feature_names=full_feature_names, - config=config, - ) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py deleted file mode 100644 index 7b0e85477a4..00000000000 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb_one.py +++ /dev/null @@ -1,1121 +0,0 @@ -# Copyright 2026 The Feast Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Native MongoDB Offline Store Implementation. - -This module implements a MongoDB offline store using native MQL aggregation -pipelines. It uses a single-collection schema where all feature views share -one collection. It is event-based: each document represents an observation -of a FeatureView at a specific point in time. Each document may contain a -subset (0 or more) of the features defined in that FeatureView, all sharing -a single event_timestamp. - -Collection Index: - db.feature_history.create_index([ - ("feature_view", ASCENDING), - ("entity_id", ASCENDING), - ("event_timestamp", DESCENDING), - ]) - -Document Schema (example): - { - "_id": ObjectId(), - "entity_id": "", - "feature_view": "driver_stats", - "features": { - "rating": 4.91, - "trips_last_7d": 132 - }, - "event_timestamp": ISODate("2026-01-20T12:00:00Z"), - "created_at": ISODate("2026-01-20T12:00:05Z") - } - -Feature Freshness Semantics: - This implementation operates at *document-level freshness*, not - per-feature freshness. During retrieval (e.g. point-in-time joins), - the system selects the most recent document for a given - (entity_id, feature_view) that satisfies time constraints, and then - extracts all requested features from that document. - - As a result, if a newer document contains only a subset of features, - missing features will be returned as NULL—even if older documents - contained values for those features. The system does not backfill - individual feature values from earlier events. - - This behavior matches common Feast offline store semantics, but may - differ from systems that compute "latest value per feature". - -Schema Evolution ("Feature Creep"): - Because features are stored in a flexible subdocument, different - documents for the same FeatureView may contain different sets of - feature fields over time. This supports: - - adding new features without backfilling historical data - - partial writes or sparse feature computation - - However, it also implies: - - newly added features will be NULL for older events - - partially populated documents may lead to NULL values even - when older data contained those features - - Users should ensure that feature computation pipelines write - complete feature sets when consistent availability is required. - -Notes: - - Entity keys are serialized to ensure consistency with Feast’s - online store and to avoid type ambiguity. - - Point-in-time correctness is enforced per FeatureView. - - TTL (time-to-live) constraints are applied per FeatureView during - historical retrieval. -""" - -import json -import warnings -from collections import defaultdict -from datetime import datetime, timezone -from typing import ( - Any, - Callable, - Dict, - Generator, - Iterable, - List, - Optional, - Tuple, - Union, -) - -import pandas as pd -import pyarrow - -try: - from pymongo import MongoClient -except ImportError: - MongoClient = None # type: ignore[assignment,misc] - -from pydantic import StrictStr - -from feast.data_source import DataSource -from feast.errors import ( - DataSourceNoNameException, - FeastExtrasDependencyImportError, - SavedDatasetLocationAlreadyExists, -) -from feast.feature_view import FeatureView -from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store import DRIVER_METADATA -from feast.infra.offline_stores.offline_store import ( - OfflineStore, - RetrievalJob, - RetrievalMetadata, -) -from feast.infra.offline_stores.offline_utils import ( - get_expected_join_keys, - infer_event_timestamp_from_entity_df, -) -from feast.infra.registry.base_registry import BaseRegistry -from feast.protos.feast.core.DataSource_pb2 import DataSource as DataSourceProto -from feast.protos.feast.core.SavedDataset_pb2 import ( - SavedDatasetStorage as SavedDatasetStorageProto, -) -from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto -from feast.protos.feast.types.Value_pb2 import Value as ValueProto -from feast.repo_config import FeastConfigBaseModel, RepoConfig -from feast.saved_dataset import SavedDatasetStorage -from feast.type_map import mongodb_to_feast_value_type -from feast.value_type import ValueType - - -class MongoDBOfflineStoreOneConfig(FeastConfigBaseModel): - """Configuration for the MongoDB offline store (single shared collection).""" - - type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one.MongoDBOfflineStoreOne" - """Offline store type selector""" - - connection_string: StrictStr = "mongodb://localhost:27017" - """MongoDB connection URI""" - - database: StrictStr = "feast" - """MongoDB database name""" - - collection: StrictStr = "feature_history" - """Single collection name for all feature views""" - - -class MongoDBSourceOne(DataSource): - """A MongoDB data source for the single-collection offline store. - - Unlike MongoDBSourceMany, this source does not map each FeatureView to - its own collection. Instead, all FeatureViews share a single MongoDB - collection (configured at the store level). - - Each document in that collection includes a ``feature_view`` field that - identifies which FeatureView it belongs to. The ``name`` of this data - source corresponds to that value and is used to filter documents during - queries. - """ - - def __init__( - self, - name: Optional[str] = None, - timestamp_field: str = "event_timestamp", - created_timestamp_column: str = "created_at", - field_mapping: Optional[Dict[str, str]] = None, - description: Optional[str] = "", - tags: Optional[Dict[str, str]] = None, - owner: Optional[str] = "", - ): - if name is None: - raise DataSourceNoNameException() - - super().__init__( - name=name, - timestamp_field=timestamp_field, - created_timestamp_column=created_timestamp_column, - field_mapping=field_mapping, - description=description, - tags=tags, - owner=owner, - ) - - def __hash__(self): - return super().__hash__() - - def __eq__(self, other): - if not isinstance(other, MongoDBSourceOne): - raise TypeError( - "Comparisons should only involve MongoDBSourceOne class objects." - ) - return ( - super().__eq__(other) - and self.timestamp_field == other.timestamp_field - and self.created_timestamp_column == other.created_timestamp_column - and self.field_mapping == other.field_mapping - ) - - @property - def feature_view_name(self) -> str: - """The feature_view discriminator value (same as source name).""" - return self.name - - def source_type(self) -> DataSourceProto.SourceType.ValueType: - return DataSourceProto.CUSTOM_SOURCE - - @staticmethod - def from_proto(data_source: DataSourceProto) -> "MongoDBSourceOne": - assert data_source.HasField("custom_options") - return MongoDBSourceOne( - name=data_source.name, - timestamp_field=data_source.timestamp_field, - created_timestamp_column=data_source.created_timestamp_column, - field_mapping=dict(data_source.field_mapping), - description=data_source.description, - tags=dict(data_source.tags), - owner=data_source.owner, - ) - - def _to_proto_impl(self) -> DataSourceProto: - return DataSourceProto( - name=self.name, - type=DataSourceProto.CUSTOM_SOURCE, - data_source_class_type="feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one.MongoDBSourceOne", - field_mapping=self.field_mapping, - custom_options=DataSourceProto.CustomSourceOptions( - configuration=json.dumps({"feature_view": self.name}).encode() - ), - description=self.description, - tags=self.tags, - owner=self.owner, - timestamp_field=self.timestamp_field, - created_timestamp_column=self.created_timestamp_column, - ) - - def validate(self, config: RepoConfig): - pass - - @staticmethod - def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: - return mongodb_to_feast_value_type - - def get_table_query_string(self) -> str: - return f"feature_history[feature_view={self.name}]" - - def get_table_column_names_and_types( - self, config: RepoConfig - ) -> Iterable[Tuple[str, str]]: - """Sample documents to infer feature names and types. - - Queries documents matching this source's feature_view name and - inspects the ``features`` subdocument to determine schema. - """ - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - connection_string = config.offline_store.connection_string - db_name = config.offline_store.database - collection_name = config.offline_store.collection - client: Any = MongoClient(connection_string, driver=DRIVER_METADATA) - try: - pipeline = [ - {"$match": {"feature_view": self.name}}, - {"$sample": {"size": 100}}, - ] - docs = list(client[db_name][collection_name].aggregate(pipeline)) - finally: - client.close() - - field_type_counts: Dict[str, Dict[str, int]] = {} - for doc in docs: - features = doc.get("features", {}) - for field, value in features.items(): - type_str = _infer_python_type_str(value) - if type_str is None: - continue - field_type_counts.setdefault(field, {}) - field_type_counts[field][type_str] = ( - field_type_counts[field].get(type_str, 0) + 1 - ) - - return [ - (field, max(counts, key=lambda t: counts[t])) - for field, counts in field_type_counts.items() - ] - - -class SavedDatasetMongoDBStorageOne(SavedDatasetStorage): - """Persists a Feast SavedDataset as a flat collection in MongoDB (one-store schema).""" - - _proto_attr_name = "custom_storage" - - def __init__(self, database: str, collection: str): - self._database = database - self._collection = collection - - @staticmethod - def from_proto( - storage_proto: SavedDatasetStorageProto, - ) -> "SavedDatasetMongoDBStorageOne": - config = json.loads(storage_proto.custom_storage.configuration) - return SavedDatasetMongoDBStorageOne( - database=config["database"], - collection=config["collection"], - ) - - def to_proto(self) -> SavedDatasetStorageProto: - return SavedDatasetStorageProto( - custom_storage=DataSourceProto.CustomSourceOptions( - configuration=json.dumps( - {"database": self._database, "collection": self._collection} - ).encode() - ) - ) - - def to_data_source(self) -> DataSource: - return MongoDBSourceOne(name=self._collection) - - -def _infer_python_type_str(value: Any) -> Optional[str]: - """Infer a Feast-compatible type string from a Python value.""" - if value is None: - return None - if isinstance(value, bool): - return "bool" - if isinstance(value, int): - return "int" - if isinstance(value, float): - return "float" - if isinstance(value, str): - return "str" - if isinstance(value, bytes): - return "bytes" - if isinstance(value, datetime): - return "datetime" - if isinstance(value, list): - if not value: - return "list[str]" - elem_type = _infer_python_type_str(value[0]) - if elem_type: - return f"list[{elem_type}]" - return "list[str]" - return None - - -def _fetch_documents( - client: Any, - database: str, - collection: str, - pipeline: List[Dict], -) -> List[Dict]: - """Execute an aggregation pipeline and return documents.""" - return list(client[database][collection].aggregate(pipeline)) - - -class MongoDBOneRetrievalJob(RetrievalJob): - """Retrieval job for native MongoDB offline store queries.""" - - def __init__( - self, - query_fn: Callable[[], pyarrow.Table], - full_feature_names: bool, - config: RepoConfig, - on_demand_feature_views: Optional[List[Any]] = None, - metadata: Optional[RetrievalMetadata] = None, - ): - self._query_fn = query_fn - self._full_feature_names = full_feature_names - self._config = config - self._on_demand_feature_views = on_demand_feature_views or [] - self._metadata = metadata - - @property - def full_feature_names(self) -> bool: - return self._full_feature_names - - @property - def on_demand_feature_views(self) -> List[Any]: - return self._on_demand_feature_views - - def _to_df_internal(self, timeout: Optional[int] = None) -> pd.DataFrame: - return self._to_arrow_internal(timeout).to_pandas() - - def _to_arrow_internal(self, timeout: Optional[int] = None) -> pyarrow.Table: - return self._query_fn() - - @property - def metadata(self) -> Optional[RetrievalMetadata]: - return self._metadata - - def persist( - self, - storage: SavedDatasetStorage, - allow_overwrite: bool = False, - timeout: Optional[int] = None, - ) -> None: - if not isinstance(storage, SavedDatasetMongoDBStorageOne): - raise ValueError( - f"MongoDBOneRetrievalJob.persist expected SavedDatasetMongoDBStorageOne, " - f"got {type(storage).__name__!r}." - ) - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - - db_name = storage._database or self._config.offline_store.database - coll_name = storage._collection - location = f"{db_name}.{coll_name}" - - client: Any = MongoClient( - self._config.offline_store.connection_string, - driver=DRIVER_METADATA, - tz_aware=True, - ) - try: - coll = client[db_name][coll_name] - if not allow_overwrite and coll.estimated_document_count() > 0: - raise SavedDatasetLocationAlreadyExists(location=location) - if allow_overwrite: - coll.drop() - records = self._to_arrow_internal().to_pylist() - if records: - coll.insert_many(records) - finally: - client.close() - - -def _serialize_entity_key_from_row( - row: pd.Series, - join_keys: List[str], - entity_key_serialization_version: int, - join_key_types: Optional[Dict[str, "ValueType"]] = None, -) -> bytes: - """Serialize entity key from a DataFrame row. - - Args: - row: DataFrame row containing join key values. - join_keys: Names of the join key columns. - entity_key_serialization_version: Version of entity key serialization. - join_key_types: Declared ValueType per join key, derived from the - FeatureView's entity_columns. When provided the correct proto field - is used (e.g. INT32 → int32_val). Without this, Python ``int`` - always maps to int64_val, which silently mismatches stored keys for - INT32 entities. - """ - entity_key = EntityKeyProto() - for key in sorted(join_keys): - entity_key.join_keys.append(key) - value = row[key] - val = ValueProto() - if hasattr(value, "item"): - value = value.item() # Convert numpy scalar to Python native (numpy 2.0+) - declared_type = join_key_types.get(key) if join_key_types else None - if declared_type is not None: - if declared_type == ValueType.INT32: - val.int32_val = int(value) - elif declared_type == ValueType.INT64: - val.int64_val = int(value) - elif declared_type == ValueType.STRING: - val.string_val = str(value) - elif declared_type == ValueType.BYTES: - val.bytes_val = bytes(value) - elif declared_type == ValueType.UNIX_TIMESTAMP: - val.unix_timestamp_val = int(value) - else: - # No declared type: infer from Python runtime type. - # Python int is always mapped to int64_val, so INT32 entities - # require join_key_types to serialise correctly. - if isinstance(value, bool): - val.bool_val = value - elif isinstance(value, int): - val.int64_val = value - elif isinstance(value, str): - val.string_val = value - elif isinstance(value, float): - val.double_val = value - else: - val.string_val = str(value) - entity_key.entity_values.append(val) - return serialize_entity_key(entity_key, entity_key_serialization_version) - - -# Module-level cache of (connection_string, db_name, collection_name) tuples for which -# indexes have already been created in this process. -_indexes_ensured: set = set() - - -class MongoDBOfflineStoreOne(OfflineStore): - """Native MongoDB offline store using single-collection schema. - - All feature views share one collection (``feature_history``), with documents - containing: - - ``entity_id``: serialized entity key (bytes) - - ``feature_view``: field matching FeatureView name - - ``features``: subdocument with feature name/value pairs - - ``event_timestamp``: event time - - ``created_at``: ingestion time - """ - - @staticmethod - def _ensure_indexes(client: Any, db_name: str, collection_name: str) -> None: - """Create recommended indexes on the feature_history collection. - - Uses create_index with background=True. If index already exists - (with same or different name), this is a no-op. - """ - collection = client[db_name][collection_name] - # Check if an equivalent index already exists - existing_indexes = collection.index_information() - # created_at is included so the full sort used by pull_latest_from_table_or_query - # {entity_id, event_timestamp DESC, created_at DESC} is satisfied entirely from - # the index (feature_view is bridged by the equality match). This matters for - # data corrections: a corrected document shares the same event_timestamp as the - # original but has a later created_at, and must win the $first selection. - target_key = [ - ("entity_id", 1), - ("feature_view", 1), - ("event_timestamp", -1), - ("created_at", -1), - ] - - for idx_info in existing_indexes.values(): - if idx_info.get("key") == target_key: - return # Index already exists - - collection.create_index( - target_key, - name="entity_fv_ts_idx", - background=True, - ) - - @staticmethod - def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: - """Get a MongoClient and ensure indexes exist once per (connection, db, collection).""" - if MongoClient is None: - raise FeastExtrasDependencyImportError( - "mongodb", "pymongo is not installed." - ) - client: Any = MongoClient( - config.offline_store.connection_string, driver=DRIVER_METADATA - ) - - cache_key = ( - config.offline_store.connection_string, - config.offline_store.database, - config.offline_store.collection, - ) - if cache_key not in _indexes_ensured: - MongoDBOfflineStoreOne._ensure_indexes( - client, - config.offline_store.database, - config.offline_store.collection, - ) - _indexes_ensured.add(cache_key) - - return client - - @staticmethod - def offline_write_batch( - config: RepoConfig, - feature_view: FeatureView, - table: pyarrow.Table, - progress: Optional[Callable[[int], Any]], - ) -> None: - """Write a batch of feature observations into the feature_history collection. - - This is the write counterpart to get_historical_features. Each row in - *table* is stored as one document using the single-collection schema:: - - { - "entity_id": , - "feature_view": , - "features": {: , ...}, - "event_timestamp": , - "created_at": , - } - - Called by FeatureStore.write_to_offline_store() and the Arrow Flight - offline server. Multiple writes for the same (entity, feature_view, - event_timestamp) are permitted — documents are appended, not replaced. - pull_latest_from_table_or_query resolves ties by picking the highest - created_at, so data corrections written with a later created_at will win. - - Args: - config: Feast repo configuration. - feature_view: The feature view being written; must have a - MongoDBSourceOne batch source. - table: Arrow table whose columns are the join key columns, feature - columns, event_timestamp, and optionally created_at. - progress: Optional callback invoked with the number of rows written - after each batch insert. - """ - if not isinstance(feature_view.batch_source, MongoDBSourceOne): - raise ValueError( - f"MongoDBOfflineStoreOne.offline_write_batch expected a MongoDBSourceOne " - f"batch source, got {type(feature_view.batch_source).__name__!r}." - ) - - entity_key_version = config.entity_key_serialization_version - db_name = config.offline_store.database - collection_name = config.offline_store.collection - - # Join key names and declared types — same derivation as get_historical_features. - join_key_types: Dict[str, ValueType] = { - feature_view.projection.join_key_map.get( - ec.name, ec.name - ): ec.dtype.to_value_type() - for ec in feature_view.entity_columns - } - join_keys = list(join_key_types.keys()) - - timestamp_field = feature_view.batch_source.timestamp_field - created_ts_col: Optional[str] = ( - feature_view.batch_source.created_timestamp_column or None - ) - - # Feature columns are everything that is not a join key or a timestamp. - reserved = set(join_keys) | {timestamp_field} - if created_ts_col: - reserved.add(created_ts_col) - feature_cols = [c for c in table.column_names if c not in reserved] - - df = table.to_pandas() - - # Ensure timestamp columns are tz-aware UTC. - for ts_col in [timestamp_field] + ([created_ts_col] if created_ts_col else []): - if ts_col in df.columns: - if not pd.api.types.is_datetime64_any_dtype(df[ts_col]): - df[ts_col] = pd.to_datetime(df[ts_col], utc=True) - elif df[ts_col].dt.tz is None: - df[ts_col] = df[ts_col].dt.tz_localize("UTC") - - # Serialize entity keys using the declared types (avoids INT32 vs INT64 mismatch). - df["_entity_id"] = df.apply( - lambda row: _serialize_entity_key_from_row( - row, join_keys, entity_key_version, join_key_types - ), - axis=1, - ) - - now = datetime.now(tz=timezone.utc) - - docs = [] - for _, row in df.iterrows(): - features: Dict[str, Any] = {} - for col in feature_cols: - val = row[col] - if pd.isna(val): - continue - if hasattr(val, "item"): - val = val.item() # numpy scalar → Python native for BSON - features[col] = val - - created_at = now - if created_ts_col and created_ts_col in df.columns: - ct = row[created_ts_col] - if not pd.isna(ct): - created_at = ( - ct.to_pydatetime() if hasattr(ct, "to_pydatetime") else ct - ) - - docs.append( - { - "entity_id": row["_entity_id"], - "feature_view": feature_view.name, - "features": features, - "event_timestamp": ( - row[timestamp_field].to_pydatetime() - if hasattr(row[timestamp_field], "to_pydatetime") - else row[timestamp_field] - ), - "created_at": created_at, - } - ) - - client = MongoDBOfflineStoreOne._get_client_and_ensure_indexes(config) - try: - coll = client[db_name][collection_name] - BATCH_SIZE = 10_000 - for i in range(0, len(docs), BATCH_SIZE): - batch = docs[i : i + BATCH_SIZE] - coll.insert_many(batch, ordered=False) - if progress: - progress(len(batch)) - finally: - client.close() - - @staticmethod - def pull_latest_from_table_or_query( - config: RepoConfig, - data_source: DataSource, - join_key_columns: List[str], - feature_name_columns: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str], - start_date: datetime, - end_date: datetime, - ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSourceOne): - raise ValueError( - f"MongoDBOfflineStoreOne expected MongoDBSourceOne, " - f"got {type(data_source).__name__!r}." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - - db_name = config.offline_store.database - collection = config.offline_store.collection - feature_view_name = data_source.feature_view_name - - start_utc = start_date.astimezone(tz=timezone.utc) - end_utc = end_date.astimezone(tz=timezone.utc) - - # Build projection to flatten features subdoc to top-level fields - project_stage: Dict[str, Any] = { - "_id": 0, - "entity_id": "$doc.entity_id", - "event_timestamp": "$doc.event_timestamp", - } - if created_timestamp_column: - project_stage["created_at"] = "$doc.created_at" - for feat in feature_name_columns: - project_stage[feat] = f"$doc.features.{feat}" - - # Build aggregation pipeline - pipeline: List[Dict[str, Any]] = [ - { - "$match": { - "feature_view": feature_view_name, - "event_timestamp": {"$gte": start_utc, "$lte": end_utc}, - } - }, - # entity_id is first in the sort so the compound index - # (entity_id, feature_view, event_timestamp DESC, created_at DESC) - # can back this stage entirely. Documents of the same entity_id - # are then contiguous, so $first correctly picks the one with the - # highest event_timestamp (and highest created_at as a tiebreaker - # for data corrections written at the same event_timestamp). - {"$sort": {"entity_id": 1, "event_timestamp": -1, "created_at": -1}}, - { - "$group": { - "_id": "$entity_id", - "doc": {"$first": "$$ROOT"}, - } - }, - {"$project": project_stage}, - ] - - def _run() -> pyarrow.Table: - client = MongoDBOfflineStoreOne._get_client_and_ensure_indexes(config) - try: - docs = _fetch_documents(client, db_name, collection, pipeline) - if not docs: - return pyarrow.Table.from_pydict({}) - - df = pd.DataFrame(docs) - if not df.empty and "event_timestamp" in df.columns: - if df["event_timestamp"].dt.tz is None: - df["event_timestamp"] = pd.to_datetime( - df["event_timestamp"], utc=True - ) - return pyarrow.Table.from_pandas(df, preserve_index=False) - finally: - client.close() - - return MongoDBOneRetrievalJob( - query_fn=_run, full_feature_names=False, config=config - ) - - @staticmethod - def pull_all_from_table_or_query( - config: RepoConfig, - data_source: DataSource, - join_key_columns: List[str], - feature_name_columns: List[str], - timestamp_field: str, - created_timestamp_column: Optional[str] = None, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - ) -> RetrievalJob: - if not isinstance(data_source, MongoDBSourceOne): - raise ValueError( - f"MongoDBOfflineStoreOne expected MongoDBSourceOne, " - f"got {type(data_source).__name__!r}." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - - db_name = config.offline_store.database - collection = config.offline_store.collection - feature_view_name = data_source.feature_view_name - - # Build match filter: feature_view + optional time range - match_filter: Dict[str, Any] = {"feature_view": feature_view_name} - if start_date or end_date: - ts_filter: Dict[str, Any] = {} - if start_date: - ts_filter["$gte"] = start_date.astimezone(tz=timezone.utc) - if end_date: - ts_filter["$lte"] = end_date.astimezone(tz=timezone.utc) - match_filter["event_timestamp"] = ts_filter - - # Build projection: flatten features subdoc to top-level fields - # This uses $getField to extract each feature from the features subdoc - project_stage: Dict[str, Any] = { - "_id": 0, - "entity_id": 1, - "event_timestamp": 1, - } - if created_timestamp_column: - project_stage["created_at"] = 1 - for feat in feature_name_columns: - project_stage[feat] = f"$features.{feat}" - - # Simple range scan pipeline - no sorting for efficiency - pipeline: List[Dict[str, Any]] = [ - {"$match": match_filter}, - {"$project": project_stage}, - ] - - def _run() -> pyarrow.Table: - client = MongoDBOfflineStoreOne._get_client_and_ensure_indexes(config) - try: - docs = _fetch_documents(client, db_name, collection, pipeline) - if not docs: - return pyarrow.Table.from_pydict({}) - - df = pd.DataFrame(docs) - if not df.empty and "event_timestamp" in df.columns: - if df["event_timestamp"].dt.tz is None: - df["event_timestamp"] = pd.to_datetime( - df["event_timestamp"], utc=True - ) - return pyarrow.Table.from_pandas(df, preserve_index=False) - finally: - client.close() - - return MongoDBOneRetrievalJob( - query_fn=_run, full_feature_names=False, config=config - ) - - @staticmethod - def get_historical_features( - config: RepoConfig, - feature_views: List[FeatureView], - feature_refs: List[str], - entity_df: Union[pd.DataFrame, str], - registry: BaseRegistry, - project: str, - full_feature_names: bool = False, - ) -> RetrievalJob: - """Fetch historical features using a "fetch + pandas join" strategy. - - Instead of using $lookup (which scales poorly), this: - 1. Extracts unique entity_ids and computes timestamp bounds - 2. Fetches all matching feature data in batched queries - 3. Uses pd.merge_asof for efficient point-in-time joins in Python - - For large entity_df, processing is chunked to bound memory usage. - """ - if isinstance(entity_df, str): - raise ValueError( - "MongoDBOfflineStoreOne does not support SQL entity_df strings. " - "Pass a pandas DataFrame instead." - ) - warnings.warn( - "MongoDB offline store (native) is in preview. API may change without notice.", - RuntimeWarning, - ) - - db_name = config.offline_store.database - feature_collection = config.offline_store.collection - entity_key_version = config.entity_key_serialization_version - - entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) - event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) - - # Map "feature_view:feature" refs → {fv_name: [feature, ...]} - fv_to_features: Dict[str, List[str]] = defaultdict(list) - for ref in feature_refs: - fv_name, feat_name = ref.split(":", 1) - fv_to_features[fv_name].append(feat_name) - - fv_by_name = {fv.name: fv for fv in feature_views} - # Join keys resolved per FV. Using the union across all FVs would produce - # serialized bytes that never match stored documents when FVs have - # heterogeneous join keys (e.g. FV1 uses driver_id, FV2 uses customer_id). - fv_join_keys_by_name: Dict[str, List[str]] = { - fv.name: list(get_expected_join_keys(project, [fv], registry)) - for fv in feature_views - } - # Declared ValueType per join key, derived directly from entity_columns on - # the FeatureView (no registry call needed). Used by - # _serialize_entity_key_from_row to pick the correct proto field so that - # INT32 entities produce int32_val bytes instead of int64_val bytes. - fv_join_key_types_by_name: Dict[str, Dict[str, ValueType]] = { - fv.name: { - fv.projection.join_key_map.get( - ec.name, ec.name - ): ec.dtype.to_value_type() - for ec in fv.entity_columns - } - for fv in feature_views - } - - # Chunk size for entity_df processing (bounds memory usage) - CHUNK_SIZE = 50_000 - # Batch size for MongoDB $in queries - MONGO_BATCH_SIZE = 10_000 - - def _chunk_dataframe( - df: pd.DataFrame, size: int - ) -> Generator[pd.DataFrame, None, None]: - """Yield successive chunks of a DataFrame.""" - for i in range(0, len(df), size): - yield df.iloc[i : i + size] - - def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: - """Process a single chunk of entity_df and return joined features. - - Args: - entity_subset_df: Chunk of entity DataFrame to process - coll: MongoDB collection object (reused across chunks) - """ - # Copy is required: iloc yields a view, and we both normalise the - # timestamp column in-place and add a temporary _entity_id column. - # Modifying the view directly would corrupt working_df and raise - # SettingWithCopyWarning. - result = entity_subset_df.copy() - # Convert timestamp column to datetime if needed - if not pd.api.types.is_datetime64_any_dtype(result[event_timestamp_col]): - result[event_timestamp_col] = pd.to_datetime( - result[event_timestamp_col], utc=True - ) - elif result[event_timestamp_col].dt.tz is None: - result[event_timestamp_col] = pd.to_datetime( - result[event_timestamp_col], utc=True - ) - - max_ts = result[event_timestamp_col].max() - - # Sort once; merge_asof requires a sorted left table. - result = result.sort_values(event_timestamp_col).reset_index(drop=True) - - # Perform PIT join per feature view. Each FV serializes entity IDs - # using only its own join keys so the bytes match what was stored. - for fv_name, features in fv_to_features.items(): - fv = fv_by_name.get(fv_name) - fv_join_keys = fv_join_keys_by_name[fv_name] - fv_join_key_types = fv_join_key_types_by_name[fv_name] - - result["_fv_entity_id"] = result.apply( - lambda row: _serialize_entity_key_from_row( - row, fv_join_keys, entity_key_version, fv_join_key_types - ), - axis=1, - ) - - unique_entity_ids = result["_fv_entity_id"].unique().tolist() - - # Per-FV TTL as the query lower bound. - ts_filter: Dict[str, Any] = {"$lte": max_ts} - if fv and fv.ttl: - ts_filter["$gte"] = max_ts - fv.ttl - - fv_docs: List[Dict] = [] - for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): - batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] - query = { - "entity_id": {"$in": batch_ids}, - "feature_view": fv_name, - "event_timestamp": ts_filter, - } - fv_docs.extend(list(coll.find(query, {"_id": 0}))) - - if not fv_docs: - for feat in features: - col_name = f"{fv_name}__{feat}" if full_feature_names else feat - result[col_name] = None - result = result.drop(columns=["_fv_entity_id"], errors="ignore") - continue - - fv_df = pd.DataFrame(fv_docs) - fv_df = fv_df.rename(columns={"entity_id": "_fv_entity_id"}) - - if "features" in fv_df.columns: - # Extract only the feature columns that were requested for - # this FV. json_normalize would expand *every* key ever - # present in any document (schema evolution means different - # documents carry different keys), producing many sparse - # columns and unnecessary memory pressure. - for feat in features: - fv_df[feat] = fv_df["features"].apply( - lambda d, f=feat: d.get(f) if isinstance(d, dict) else None - ) - fv_df = fv_df.drop(columns=["features"]) - - if fv_df["event_timestamp"].dt.tz is None: - fv_df["event_timestamp"] = pd.to_datetime( - fv_df["event_timestamp"], utc=True - ) - - # merge_asof requires the right-hand DataFrame to be sorted by - # its join key. After the rename below, that key is _fv_ts; - # we sort on event_timestamp here (before the rename) so the - # physical order is correct when merge_asof consumes the frame. - fv_df = fv_df.sort_values("event_timestamp").reset_index(drop=True) - - merge_cols = ["_fv_entity_id", "event_timestamp"] + [ - f for f in features if f in fv_df.columns - ] - # .copy() is required: column selection on a DataFrame returns - # a view; calling rename() on that view raises - # SettingWithCopyWarning and can produce unexpected results. - # The copy ensures we own the data before mutating it. - fv_df_subset = fv_df[ - [c for c in merge_cols if c in fv_df.columns] - ].copy() - fv_df_subset = fv_df_subset.rename( - columns={"event_timestamp": "_fv_ts"} - ) - - # Prefix feature columns to avoid collisions when FVs share names. - fv_prefix = f"__fv_{fv_name}__" - temp_rename = { - f: f"{fv_prefix}{f}" for f in features if f in fv_df_subset.columns - } - fv_df_subset = fv_df_subset.rename(columns=temp_rename) - - result = pd.merge_asof( - result, - fv_df_subset, - left_on=event_timestamp_col, - right_on="_fv_ts", - by="_fv_entity_id", - direction="backward", - ) - - # Apply TTL - if fv and fv.ttl: - cutoff = result[event_timestamp_col] - fv.ttl - stale_mask = result["_fv_ts"] < cutoff - for feat in features: - temp_col = f"{fv_prefix}{feat}" - if temp_col in result.columns: - result.loc[stale_mask, temp_col] = None - - for feat in features: - temp_col = f"{fv_prefix}{feat}" - col_name = f"{fv_name}__{feat}" if full_feature_names else feat - if temp_col in result.columns: - if col_name in result.columns: - result = result.drop(columns=[col_name]) - result = result.rename(columns={temp_col: col_name}) - elif col_name not in result.columns: - result[col_name] = None - - result = result.drop( - columns=["_fv_entity_id", "_fv_ts"], errors="ignore" - ) - - return result - - def _run() -> pyarrow.Table: - # Add row index to preserve original ordering - # Copy is required: we write _row_idx into this DataFrame to - # restore original ordering after chunk results are concatenated, - # and must not mutate the caller's entity_df. - working_df = entity_df.copy() - working_df["_row_idx"] = range(len(working_df)) - - # Create client once for all chunks - client = MongoDBOfflineStoreOne._get_client_and_ensure_indexes(config) - try: - coll = client[db_name][feature_collection] - - if len(working_df) <= CHUNK_SIZE: - # Small workload: process in single pass - result_df = _run_single(working_df, coll) - else: - # Large workload: process in chunks - chunk_results = [] - for chunk in _chunk_dataframe(working_df, CHUNK_SIZE): - chunk_results.append(_run_single(chunk, coll)) - - result_df = pd.concat(chunk_results, ignore_index=True) - finally: - client.close() - - # Restore original ordering and remove index column - result_df = result_df.sort_values("_row_idx").reset_index(drop=True) - result_df = result_df.drop(columns=["_row_idx"], errors="ignore") - - # Ensure timestamp column is tz-aware - if not result_df.empty and event_timestamp_col in result_df.columns: - if result_df[event_timestamp_col].dt.tz is None: - result_df[event_timestamp_col] = pd.to_datetime( - result_df[event_timestamp_col], utc=True - ) - - return pyarrow.Table.from_pandas(result_df, preserve_index=False) - - return MongoDBOneRetrievalJob( - query_fn=_run, - full_feature_names=full_feature_names, - config=config, - ) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py deleted file mode 100644 index fa09fe22106..00000000000 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark.py +++ /dev/null @@ -1,2002 +0,0 @@ -""" -Performance benchmarks comparing MongoDB offline store implementations: Many vs One. - -- Many: One collection per FeatureView (MongoDBOfflineStoreMany) -- One: Single shared collection for all FeatureViews (MongoDBOfflineStoreOne) - -These tests measure performance across different scaling dimensions: -1. Row count scaling (entity_df size) -2. Feature width scaling (features per FeatureView) -3. Entity distribution (unique vs skewed/repeated entity_ids) - -Metrics captured: -- Runtime (wall clock) -- Memory (peak Python memory via tracemalloc) -- MongoDB server metrics (opcounters, execution stats) - -Run with: pytest benchmark.py -v -s -Skip slow tests: pytest benchmark.py -v -s -m "not slow" -""" - -import resource -import sys -import time -import tracemalloc -from dataclasses import dataclass, field -from datetime import datetime, timedelta -from typing import Any, Dict, Generator, List, Optional - -import pandas as pd -import pytest -import pytz - -pytest.importorskip("pymongo") - -from unittest.mock import MagicMock - -from pymongo import MongoClient -from testcontainers.mongodb import MongoDbContainer - -from feast import Entity, FeatureView, Field -from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( - MongoDBOfflineStoreMany, - MongoDBOfflineStoreManyConfig, - MongoDBSourceMany, -) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( - MongoDBOfflineStoreOne, - MongoDBOfflineStoreOneConfig, - MongoDBSourceOne, -) -from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto -from feast.protos.feast.types.Value_pb2 import Value as ValueProto -from feast.repo_config import RepoConfig -from feast.types import Float64, Int64 -from feast.value_type import ValueType - -# Check if Docker is available -docker_available = False -try: - import docker - - try: - client = docker.from_env() - client.ping() - docker_available = True - except Exception: - pass -except ImportError: - pass - -_requires_docker = pytest.mark.skipif( - not docker_available, - reason="Docker is not available or not running.", -) - -ENTITY_KEY_VERSION = 3 - - -@dataclass -class BenchmarkResult: - """Container for benchmark results.""" - - implementation: str - test_name: str - dimension: str - value: int - duration_seconds: float - rows_per_second: float - peak_memory_mb: float = 0.0 - mongo_docs_examined: int = 0 - mongo_keys_examined: int = 0 - mongo_execution_time_ms: int = 0 - - -@dataclass -class MongoMetrics: - """MongoDB server metrics captured before/after a query.""" - - opcounters: Dict[str, int] = field(default_factory=dict) - docs_examined: int = 0 - keys_examined: int = 0 - - @staticmethod - def capture(client: Any) -> "MongoMetrics": - """Capture current MongoDB server metrics.""" - status = client.admin.command("serverStatus") - return MongoMetrics( - opcounters=dict(status.get("opcounters", {})), - ) - - def delta(self, after: "MongoMetrics") -> Dict[str, int]: - """Calculate delta between two metric snapshots.""" - return { - k: after.opcounters.get(k, 0) - self.opcounters.get(k, 0) - for k in after.opcounters - } - - -def _make_entity_id(driver_id: int) -> bytes: - """Create serialized entity key.""" - entity_key = EntityKeyProto() - entity_key.join_keys.append("driver_id") - val = ValueProto() - val.int64_val = driver_id - entity_key.entity_values.append(val) - return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) - - -# --------------------------------------------------------------------------- -# RSS measurement (per-operation delta) -# --------------------------------------------------------------------------- - - -def _rss_mb() -> float: - """Return current process RSS in MB. - - On macOS ``ru_maxrss`` is in bytes; on Linux it is in kilobytes. - Note: ``ru_maxrss`` is a *lifetime peak* reported by the kernel. We - snapshot it before and after each operation to compute the growth - attributable to that operation. Because the peak never decreases, the - delta accurately reflects the high-water-mark added by that operation - even after GC has freed Python objects. - """ - rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss - if sys.platform == "darwin": - return rss / (1024 * 1024) - return rss / 1024 # Linux: KB → MB - - -# --------------------------------------------------------------------------- -# Batched data generators (memory-efficient at N > 100 K) -# --------------------------------------------------------------------------- - -_INSERT_BATCH = 50_000 # docs per insert_many call - - -def _generate_many_data_batched( - client: Any, - db_name: str, - collection_name: str, - num_entities: int, - num_features: int, - rows_per_entity: int = 5, -) -> datetime: - """Generate Many-schema data using batched inserts. - - Pre-computes one timestamp per historical row so we avoid calling - ``timedelta`` inside the inner loop. Feature values are simple - floats derived from (entity, row, feature) — no numpy required. - """ - import numpy as np - - collection = client[db_name][collection_name] - collection.drop() - - now = datetime.now(tz=pytz.UTC) - timestamps = [now - timedelta(hours=r) for r in range(rows_per_entity)] - feat_names = [f"feature_{f}" for f in range(num_features)] - - # Pre-generate all feature values as a 2-D numpy array for speed. - rng = np.random.default_rng(seed=42) - feature_matrix = rng.random((num_entities * rows_per_entity, num_features)).astype( - float - ) - - batch: List[Dict] = [] - idx = 0 - for entity_id in range(num_entities): - for row in range(rows_per_entity): - doc: Dict[str, Any] = { - "driver_id": entity_id, - "event_timestamp": timestamps[row], - } - row_vals = feature_matrix[idx] - for fi, fname in enumerate(feat_names): - doc[fname] = float(row_vals[fi]) - batch.append(doc) - idx += 1 - if len(batch) >= _INSERT_BATCH: - collection.insert_many(batch) - batch = [] - - if batch: - collection.insert_many(batch) - - return now - - -def _generate_one_data_batched( - client: Any, - db_name: str, - collection_name: str, - feature_view_name: str, - num_entities: int, - num_features: int, - rows_per_entity: int = 5, -) -> datetime: - """Generate One-schema data using batched inserts.""" - import numpy as np - - collection = client[db_name][collection_name] - # Do NOT drop — multiple feature views may share the collection. - - now = datetime.now(tz=pytz.UTC) - timestamps = [now - timedelta(hours=r) for r in range(rows_per_entity)] - feat_names = [f"feature_{f}" for f in range(num_features)] - - rng = np.random.default_rng(seed=42) - feature_matrix = rng.random((num_entities * rows_per_entity, num_features)).astype( - float - ) - - batch: List[Dict] = [] - idx = 0 - for entity_id in range(num_entities): - eid_bytes = _make_entity_id(entity_id) - for row in range(rows_per_entity): - row_vals = feature_matrix[idx] - features = { - feat_names[fi]: float(row_vals[fi]) for fi in range(num_features) - } - doc: Dict[str, Any] = { - "entity_id": eid_bytes, - "feature_view": feature_view_name, - "features": features, - "event_timestamp": timestamps[row], - "created_at": timestamps[row], - } - batch.append(doc) - idx += 1 - if len(batch) >= _INSERT_BATCH: - collection.insert_many(batch) - batch = [] - - if batch: - collection.insert_many(batch) - - return now - - -@pytest.fixture(scope="module") -def mongodb_container() -> Generator[MongoDbContainer, None, None]: - """Start a MongoDB container for benchmarks.""" - container = MongoDbContainer( - "mongo:latest", - username="test", - password="test", # pragma: allowlist secret - ).with_exposed_ports(27017) - container.start() - yield container - container.stop() - - -@pytest.fixture -def mongodb_connection_string(mongodb_container: MongoDbContainer) -> str: - """Get MongoDB connection string.""" - exposed_port = mongodb_container.get_exposed_port(27017) - return f"mongodb://test:test@localhost:{exposed_port}" # pragma: allowlist secret - - -@pytest.fixture -def many_config(mongodb_connection_string: str) -> RepoConfig: - """RepoConfig for Many implementation (one collection per FeatureView).""" - return RepoConfig( - project="benchmark", - registry="memory://", - provider="local", - offline_store=MongoDBOfflineStoreManyConfig( - connection_string=mongodb_connection_string, - database="benchmark_db", - ), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - - -@pytest.fixture -def one_config(mongodb_connection_string: str) -> RepoConfig: - """RepoConfig for One implementation (single shared collection).""" - return RepoConfig( - project="benchmark", - registry="memory://", - provider="local", - offline_store=MongoDBOfflineStoreOneConfig( - connection_string=mongodb_connection_string, - database="benchmark_db", - collection="feature_history", - ), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - - -def _generate_many_data( - client: MongoClient, - db_name: str, - collection_name: str, - num_entities: int, - num_features: int, - rows_per_entity: int = 5, -) -> datetime: - """Generate test data for Many (one collection per FV, flat schema).""" - collection = client[db_name][collection_name] - collection.drop() - - now = datetime.now(tz=pytz.UTC) - docs = [] - - for entity_id in range(num_entities): - for row in range(rows_per_entity): - doc = { - "driver_id": entity_id, - "event_timestamp": now - timedelta(hours=row), - } - for f in range(num_features): - doc[f"feature_{f}"] = float(entity_id * 100 + f + row * 0.1) - docs.append(doc) - - collection.insert_many(docs) - return now - - -def _generate_one_data( - client: MongoClient, - db_name: str, - collection_name: str, - feature_view_name: str, - num_entities: int, - num_features: int, - rows_per_entity: int = 5, -) -> datetime: - """Generate test data for One (single collection, nested features).""" - collection = client[db_name][collection_name] - # Don't drop - may have multiple FVs in same collection - - now = datetime.now(tz=pytz.UTC) - docs = [] - - for entity_id in range(num_entities): - for row in range(rows_per_entity): - features = {} - for f in range(num_features): - features[f"feature_{f}"] = float(entity_id * 100 + f + row * 0.1) - - doc = { - "entity_id": _make_entity_id(entity_id), - "feature_view": feature_view_name, - "features": features, - "event_timestamp": now - timedelta(hours=row), - "created_at": now - timedelta(hours=row), - } - docs.append(doc) - - collection.insert_many(docs) - return now - - -def _create_many_fv(num_features: int) -> tuple: - """Create Many source and FeatureView.""" - source = MongoDBSourceMany( - name="driver_benchmark", - database="benchmark_db", - collection="driver_benchmark", - timestamp_field="event_timestamp", - ) - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - - schema = [Field(name="driver_id", dtype=Int64)] - for f in range(num_features): - schema.append(Field(name=f"feature_{f}", dtype=Float64)) - - fv = FeatureView( - name="driver_benchmark", - entities=[entity], - schema=schema, - source=source, - ttl=timedelta(days=1), - ) - return source, fv - - -def _create_one_fv(num_features: int) -> tuple: - """Create One source and FeatureView.""" - source = MongoDBSourceOne( - name="driver_benchmark", - timestamp_field="event_timestamp", - ) - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - - schema = [Field(name="driver_id", dtype=Int64)] - for f in range(num_features): - schema.append(Field(name=f"feature_{f}", dtype=Float64)) - - fv = FeatureView( - name="driver_benchmark", - entities=[entity], - schema=schema, - source=source, - ttl=timedelta(days=1), - ) - return source, fv - - -def _run_benchmark(func, name: str) -> float: - """Run a function and return elapsed time.""" - start = time.perf_counter() - func() # Execute the function - elapsed = time.perf_counter() - start - return elapsed - - -@dataclass -class FullBenchmarkResult: - """Full benchmark results with all metrics.""" - - elapsed_seconds: float - peak_memory_mb: float # tracemalloc: Python-heap peak for this op - rss_delta_mb: float = 0.0 # RSS growth attributable to this op (OS-level) - mongo_opcounters_delta: Dict[str, int] = field(default_factory=dict) - status: str = "OK" # "OK" | "OOM" | "ERROR:" - - -def _run_benchmark_full( - func, - mongo_client: Optional[Any] = None, -) -> FullBenchmarkResult: - """Run a benchmark capturing runtime, memory (tracemalloc + RSS), and MongoDB metrics.""" - mongo_before = None - if mongo_client: - mongo_before = MongoMetrics.capture(mongo_client) - - rss_before = _rss_mb() - tracemalloc.start() - start = time.perf_counter() - - status = "OK" - try: - func() - except MemoryError: - status = "OOM" - except Exception as exc: - status = f"ERROR:{exc!s:.80}" - - elapsed = time.perf_counter() - start - _, peak_memory = tracemalloc.get_traced_memory() - tracemalloc.stop() - rss_after = _rss_mb() - - mongo_delta: Dict[str, int] = {} - if mongo_client and mongo_before: - mongo_after = MongoMetrics.capture(mongo_client) - mongo_delta = mongo_before.delta(mongo_after) - - return FullBenchmarkResult( - elapsed_seconds=elapsed, - peak_memory_mb=peak_memory / (1024 * 1024), - rss_delta_mb=max(rss_after - rss_before, 0.0), - mongo_opcounters_delta=mongo_delta, - status=status, - ) - - -def _print_benchmark_result( - impl: str, - dimension_name: str, - dimension_value: int, - result: FullBenchmarkResult, - num_rows: Optional[int] = None, -) -> None: - """Pretty print benchmark results.""" - status_tag = f" [{result.status}]" if result.status != "OK" else "" - print(f"\n[{impl}] {dimension_name}: {dimension_value:,}{status_tag}") - print(f" Time: {result.elapsed_seconds:.3f}s") - print(f" Mem (trace): {result.peak_memory_mb:.1f} MB") - print(f" Mem (RSS Δ): {result.rss_delta_mb:.1f} MB") - if num_rows and result.elapsed_seconds > 0: - print(f" Rate: {num_rows / result.elapsed_seconds:,.0f} rows/s") - if result.mongo_opcounters_delta: - print(f" Mongo ops: {result.mongo_opcounters_delta}") - - -# ============================================================================= -# Test 1: Scale Rows (entity_df size) -# ============================================================================= - -ROW_COUNTS = [ - 1000, - 5000, - 10000, -] # Reduced for CI; use [10000, 50000, 100000, 500000] for full benchmark - - -@_requires_docker -@pytest.mark.parametrize("num_rows", ROW_COUNTS) -def test_scale_rows_many( - mongodb_connection_string: str, many_config: RepoConfig, num_rows: int -) -> None: - """Benchmark Many implementation with varying entity_df sizes. - - Measures: runtime, peak memory, MongoDB opcounters. - """ - num_features = 10 - num_entities = num_rows # One row per entity for simplicity - - client = MongoClient(mongodb_connection_string) - try: - now = _generate_many_data( - client, - "benchmark_db", - "driver_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=3, - ) - - _, fv = _create_many_fv(num_features) - - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] - - def run_query(): - job = MongoDBOfflineStoreMany.get_historical_features( - config=many_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - _print_benchmark_result("IBIS", "Rows", num_rows, result, num_rows=num_rows) - - finally: - client.close() - - -@_requires_docker -@pytest.mark.parametrize("num_rows", ROW_COUNTS) -def test_scale_rows_one( - mongodb_connection_string: str, one_config: RepoConfig, num_rows: int -) -> None: - """Benchmark One implementation with varying entity_df sizes. - - Measures: runtime, peak memory, MongoDB opcounters. - """ - num_features = 10 - num_entities = num_rows - - client = MongoClient(mongodb_connection_string) - try: - client["benchmark_db"]["feature_history"].drop() - now = _generate_one_data( - client, - "benchmark_db", - "feature_history", - "driver_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=3, - ) - - _, fv = _create_one_fv(num_features) - - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] - - def run_query(): - job = MongoDBOfflineStoreOne.get_historical_features( - config=one_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - _print_benchmark_result("NATIVE", "Rows", num_rows, result, num_rows=num_rows) - - finally: - client.close() - - -# ============================================================================= -# Test 2: Wide Feature Views (features per FV) -# ============================================================================= - -FEATURE_COUNTS = [10, 50, 100] # Use [50, 100, 150, 200] for full benchmark - - -@_requires_docker -@pytest.mark.parametrize("num_features", FEATURE_COUNTS) -def test_wide_features_many( - mongodb_connection_string: str, many_config: RepoConfig, num_features: int -) -> None: - """Benchmark Many with varying feature width.""" - num_entities = 1000 - - client = MongoClient(mongodb_connection_string) - try: - now = _generate_many_data( - client, - "benchmark_db", - "driver_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=3, - ) - - _, fv = _create_many_fv(num_features) - - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] - - def run_query(): - job = MongoDBOfflineStoreMany.get_historical_features( - config=many_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - _print_benchmark_result( - "IBIS", "Features", num_features, result, num_rows=num_entities - ) - - finally: - client.close() - - -@_requires_docker -@pytest.mark.parametrize("num_features", FEATURE_COUNTS) -def test_wide_features_one( - mongodb_connection_string: str, one_config: RepoConfig, num_features: int -) -> None: - """Benchmark One with varying feature width.""" - num_entities = 1000 - - client = MongoClient(mongodb_connection_string) - try: - client["benchmark_db"]["feature_history"].drop() - now = _generate_one_data( - client, - "benchmark_db", - "feature_history", - "driver_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=3, - ) - - _, fv = _create_one_fv(num_features) - - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] - - def run_query(): - job = MongoDBOfflineStoreOne.get_historical_features( - config=one_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - _print_benchmark_result( - "NATIVE", "Features", num_features, result, num_rows=num_entities - ) - - finally: - client.close() - - -# ============================================================================= -# Test 3: Skewed Entity Distribution -# ============================================================================= - - -@_requires_docker -@pytest.mark.parametrize("unique_ratio", [1.0, 0.5, 0.1]) # 100%, 50%, 10% unique -def test_entity_skew_many( - mongodb_connection_string: str, many_config: RepoConfig, unique_ratio: float -) -> None: - """Benchmark Many with varying entity uniqueness in entity_df.""" - import numpy as np - - total_rows = 5000 - num_features = 10 - num_unique_entities = int(total_rows * unique_ratio) - num_unique_entities = max(num_unique_entities, 1) - - client = MongoClient(mongodb_connection_string) - try: - now = _generate_many_data( - client, - "benchmark_db", - "driver_benchmark", - num_entities=num_unique_entities, - num_features=num_features, - rows_per_entity=5, - ) - - _, fv = _create_many_fv(num_features) - - # Create entity_df with repeated entity_ids - entity_ids = np.random.choice( - num_unique_entities, size=total_rows, replace=True - ) - entity_df = pd.DataFrame( - { - "driver_id": entity_ids, - "event_timestamp": [ - now - timedelta(minutes=i % 60) for i in range(total_rows) - ], - } - ) - - feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] - - def run_query(): - job = MongoDBOfflineStoreMany.get_historical_features( - config=many_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - print( - f"\n[MANY] Unique ratio: {unique_ratio:.0%} ({num_unique_entities:,} unique / {total_rows:,} rows)" - ) - print(f" Time: {result.elapsed_seconds:.3f}s") - print(f" Memory: {result.peak_memory_mb:.1f} MB") - print(f" Mongo ops: {result.mongo_opcounters_delta}") - - finally: - client.close() - - -@_requires_docker -@pytest.mark.parametrize("unique_ratio", [1.0, 0.5, 0.1]) -def test_entity_skew_one( - mongodb_connection_string: str, one_config: RepoConfig, unique_ratio: float -) -> None: - """Benchmark One with varying entity uniqueness in entity_df.""" - import numpy as np - - total_rows = 5000 - num_features = 10 - num_unique_entities = int(total_rows * unique_ratio) - num_unique_entities = max(num_unique_entities, 1) - - client = MongoClient(mongodb_connection_string) - try: - client["benchmark_db"]["feature_history"].drop() - now = _generate_one_data( - client, - "benchmark_db", - "feature_history", - "driver_benchmark", - num_entities=num_unique_entities, - num_features=num_features, - rows_per_entity=5, - ) - - _, fv = _create_one_fv(num_features) - - entity_ids = np.random.choice( - num_unique_entities, size=total_rows, replace=True - ) - entity_df = pd.DataFrame( - { - "driver_id": entity_ids, - "event_timestamp": [ - now - timedelta(minutes=i % 60) for i in range(total_rows) - ], - } - ) - - feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] - - def run_query(): - job = MongoDBOfflineStoreOne.get_historical_features( - config=one_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - print( - f"\n[ONE] Unique ratio: {unique_ratio:.0%} ({num_unique_entities:,} unique / {total_rows:,} rows)" - ) - print(f" Time: {result.elapsed_seconds:.3f}s") - print(f" Memory: {result.peak_memory_mb:.1f} MB") - print(f" Mongo ops: {result.mongo_opcounters_delta}") - - finally: - client.close() - - -# ============================================================================= -# Summary comparison test -# ============================================================================= - - -@_requires_docker -def test_summary_comparison( - mongodb_connection_string: str, many_config: RepoConfig, one_config: RepoConfig -) -> None: - """Run a standard comparison and print summary with full metrics.""" - num_entities = 2000 - num_features = 20 - - client = MongoClient(mongodb_connection_string) - try: - # Setup Many data - now = _generate_many_data( - client, - "benchmark_db", - "driver_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=5, - ) - - # Setup One data - client["benchmark_db"]["feature_history"].drop() - _generate_one_data( - client, - "benchmark_db", - "feature_history", - "driver_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=5, - ) - - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - feature_refs = [f"driver_benchmark:feature_{i}" for i in range(num_features)] - - # Many benchmark - _, many_fv = _create_many_fv(num_features) - - def run_many(): - job = MongoDBOfflineStoreMany.get_historical_features( - config=many_config, - feature_views=[many_fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - many_result = _run_benchmark_full(run_many, mongo_client=client) - - # One benchmark - _, one_fv = _create_one_fv(num_features) - - def run_one(): - job = MongoDBOfflineStoreOne.get_historical_features( - config=one_config, - feature_views=[one_fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - one_result = _run_benchmark_full(run_one, mongo_client=client) - - # Print summary - print("\n" + "=" * 70) - print("SUMMARY COMPARISON: Many vs One") - print("=" * 70) - print(f"Entities: {num_entities:,} | Features: {num_features}") - print("-" * 70) - print(f"{'Metric':<20} {'Many':>20} {'One':>20}") - print("-" * 70) - print( - f"{'Time (s)':<20} {many_result.elapsed_seconds:>20.3f} {one_result.elapsed_seconds:>20.3f}" - ) - print( - f"{'Memory (MB)':<20} {many_result.peak_memory_mb:>20.1f} {one_result.peak_memory_mb:>20.1f}" - ) - print( - f"{'Rows/sec':<20} {num_entities / many_result.elapsed_seconds:>20,.0f} {num_entities / one_result.elapsed_seconds:>20,.0f}" - ) - print("-" * 70) - - if one_result.elapsed_seconds > 0: - ratio = one_result.elapsed_seconds / many_result.elapsed_seconds - faster = "Many" if ratio > 1 else "One" - print(f"{faster} is {max(ratio, 1 / ratio):.1f}x faster") - print("=" * 70) - - finally: - client.close() - - -# ============================================================================= -# Test 4: Large-Scale Memory Stress (N × M × P sweep) -# ============================================================================= -# -# Goal: demonstrate that Many's memory grows O(N × M × P) while One stays -# bounded by CHUNK_SIZE regardless of collection size. -# -# Dimensions held constant across this suite: -# M = STRESS_NUM_FEATURES = 50 (5× wider than the PR baseline) -# P = STRESS_ROWS_PER_ENTITY = 5 (slightly deeper history) -# -# Total collection docs = N × P. Memory for Many ≈ total_docs × M × 8 bytes -# × ~20 pandas/ibis overhead factor (empirically observed in PR benchmarks). -# One memory stays ≈ constant because it fetches CHUNK_SIZE entities at a time. - -STRESS_ENTITY_COUNTS = [50_000, 150_000, 300_000, 500_000] -STRESS_NUM_FEATURES = 50 -STRESS_ROWS_PER_ENTITY = 5 - - -def _create_many_fv_stress(num_features: int) -> tuple: - """Many source + FV for stress tests.""" - source = MongoDBSourceMany( - name="stress_benchmark", - database="benchmark_db", - collection="stress_benchmark", - timestamp_field="event_timestamp", - ) - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - fv = FeatureView( - name="stress_benchmark", - entities=[entity], - schema=schema, - source=source, - ttl=timedelta(days=1), - ) - return source, fv - - -def _create_one_fv_stress(num_features: int) -> tuple: - """One source + FV for stress tests.""" - source = MongoDBSourceOne( - name="stress_benchmark", timestamp_field="event_timestamp" - ) - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - fv = FeatureView( - name="stress_benchmark", - entities=[entity], - schema=schema, - source=source, - ttl=timedelta(days=1), - ) - return source, fv - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) # opt out of global 300s limit — large inserts take time -@pytest.mark.parametrize("num_entities", STRESS_ENTITY_COUNTS) -def test_scale_entities_stress_many( - mongodb_connection_string: str, many_config: RepoConfig, num_entities: int -) -> None: - """Many: memory stress across large entity counts with wide features. - - Collection has ``num_entities × STRESS_ROWS_PER_ENTITY`` total documents, - each with ``STRESS_NUM_FEATURES`` float fields. Many loads the entire - collection into memory, so peak memory grows linearly. A MemoryError is - caught and reported as "OOM" without failing the test. - """ - num_features = STRESS_NUM_FEATURES - rows_per_entity = STRESS_ROWS_PER_ENTITY - total_docs = num_entities * rows_per_entity - - client = MongoClient(mongodb_connection_string) - try: - print( - f"\n[MANY stress] Generating {total_docs:,} docs " - f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features)…" - ) - now = _generate_many_data_batched( - client, - "benchmark_db", - "stress_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=rows_per_entity, - ) - - _, fv = _create_many_fv_stress(num_features) - feature_refs = [f"stress_benchmark:feature_{i}" for i in range(num_features)] - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - safe, projected_mb, free_mb = _check_memory_for_many( - num_entities, rows_per_entity, num_features - ) - if not safe: - print( - f" [MANY stress] Skipping query — projected {projected_mb:,.0f} MB " - f"exceeds 80% of {free_mb:,.0f} MB free RAM. Would likely SIGKILL." - ) - result = FullBenchmarkResult( - elapsed_seconds=0, - peak_memory_mb=projected_mb, - status="SKIPPED:low_memory", - ) - else: - - def run_query(): - job = MongoDBOfflineStoreMany.get_historical_features( - config=many_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - - _print_benchmark_result( - "MANY", "Entities (stress)", num_entities, result, num_rows=num_entities - ) - print( - f" Collection docs: {total_docs:,} | " - f"Raw data ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" - ) - - finally: - client.close() - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) # opt out of global 300s limit — large inserts take time -@pytest.mark.parametrize("num_entities", STRESS_ENTITY_COUNTS) -def test_scale_entities_stress_one( - mongodb_connection_string: str, one_config: RepoConfig, num_entities: int -) -> None: - """One: memory stress across the same large entity counts. - - Because One fetches CHUNK_SIZE=50,000 entities at a time from MongoDB, - its peak memory is O(CHUNK_SIZE × M) regardless of N. All parametrize - values should complete without OOM. - """ - num_features = STRESS_NUM_FEATURES - rows_per_entity = STRESS_ROWS_PER_ENTITY - total_docs = num_entities * rows_per_entity - - client = MongoClient(mongodb_connection_string) - try: - client["benchmark_db"]["feature_history_stress"].drop() - print( - f"\n[ONE stress] Generating {total_docs:,} docs " - f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features)…" - ) - now = _generate_one_data_batched( - client, - "benchmark_db", - "feature_history_stress", - "stress_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=rows_per_entity, - ) - - # Use a separate RepoConfig pointing at the stress collection - stress_one_config = RepoConfig( - project="benchmark", - registry="memory://", - provider="local", - offline_store=MongoDBOfflineStoreOneConfig( - connection_string=mongodb_connection_string, - database="benchmark_db", - collection="feature_history_stress", - ), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - - _, fv = _create_one_fv_stress(num_features) - feature_refs = [f"stress_benchmark:feature_{i}" for i in range(num_features)] - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - def run_query(): - job = MongoDBOfflineStoreOne.get_historical_features( - config=stress_one_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - _print_benchmark_result( - "ONE", "Entities (stress)", num_entities, result, num_rows=num_entities - ) - print( - f" Collection docs: {total_docs:,} | " - f"Raw data ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" - ) - assert result.status == "OK", f"One should never OOM — got: {result.status}" - - finally: - client.close() - - -# ============================================================================= -# Test 5: OOM Crossover Demo -# ============================================================================= -# -# Runs BOTH implementations against the same large collection. -# - Many is expected to exhaust memory and raise MemoryError (caught → OOM). -# - One must complete successfully, demonstrating that chunked fetching -# keeps memory bounded regardless of total collection size. -# -# Scale selection: -# OOM_ENTITIES × OOM_ROWS × OOM_FEATURES total float values in MongoDB. -# Empirically, Many needs ~20 bytes per float (pandas + ibis + pyarrow copies). -# To exceed a 32 GB machine: 32 GB / 20 / 8 ≈ 200 M floats → ~400 K entities -# at (OOM_ROWS=5, OOM_FEATURES=100). -# -# If your machine has less RAM the crossover will happen at a smaller N; -# if it has more you may need to increase OOM_ENTITIES. - -OOM_ENTITIES = 400_000 # total unique entities -OOM_FEATURES = 100 # float features per document -OOM_ROWS = 5 # historical rows per entity -# → total docs = 2,000,000 | raw floats = 200,000,000 | raw bytes ≈ 1.6 GB -# → Many projected peak ≈ 32 GB (likely OOM on ≤32 GB machines) -# → One projected peak ≈ CHUNK_SIZE × OOM_FEATURES × 20 B ≈ 100 MB - - -def _available_memory_mb() -> float: - """Estimate *total* installed RAM in MB (used for reporting only).""" - try: - import subprocess - - out = subprocess.check_output(["sysctl", "-n", "hw.memsize"], text=True).strip() - return int(out) / (1024 * 1024) - except Exception: - pass - try: - with open("/proc/meminfo") as f: - for line in f: - if line.startswith("MemTotal:"): - return int(line.split()[1]) / 1024 - except Exception: - pass - return 16_384 # conservative fallback: 16 GB - - -def _free_memory_mb() -> float: - """Estimate *currently free/available* RAM in MB. - - On macOS uses ``vm_stat`` (free + purgeable pages at 16 KB each). - On Linux reads ``MemAvailable`` from ``/proc/meminfo``. - Falls back to a conservative 4 GB if neither is available. - - This is used to gate Many queries before they are attempted — avoiding - SIGKILL from the macOS jetsam memory-pressure system, which cannot be - caught by Python's ``except MemoryError``. - """ - try: - import subprocess - - out = subprocess.check_output(["vm_stat"], text=True) - page_size = 16_384 # 16 KB on Apple Silicon; 4 KB on Intel - try: - hw = subprocess.check_output( - ["sysctl", "-n", "hw.pagesize"], text=True - ).strip() - page_size = int(hw) - except Exception: - pass - free = purgeable = 0 - for line in out.splitlines(): - if line.startswith("Pages free:"): - free = int(line.split(":")[1].strip().rstrip(".")) - elif line.startswith("Pages purgeable:"): - purgeable = int(line.split(":")[1].strip().rstrip(".")) - return (free + purgeable) * page_size / (1024 * 1024) - except Exception: - pass - try: - with open("/proc/meminfo") as f: - for line in f: - if line.startswith("MemAvailable:"): - return int(line.split()[1]) / 1024 - except Exception: - pass - return 4_096 # conservative fallback: 4 GB - - -# Empirical overhead ratio: bytes consumed by Many per raw float value. -# Measured across stress test runs (tracemalloc peak / raw float count). -# 500K entities × P=5 × M=50 → 125M floats → 16.2 GB → ~130 bytes/float. -_MANY_BYTES_PER_FLOAT = 130 - - -def _many_projected_mb( - num_entities: int, rows_per_entity: int, num_features: int -) -> float: - """Project peak tracemalloc memory for a Many query in MB.""" - return num_entities * rows_per_entity * num_features * _MANY_BYTES_PER_FLOAT / 1e6 - - -def _check_memory_for_many( - num_entities: int, - rows_per_entity: int, - num_features: int, - safety_factor: float = 0.80, -) -> tuple: - """Return (safe_to_run, projected_mb, free_mb). - - ``safe_to_run`` is False when the projected peak exceeds - ``safety_factor × free_mb``, meaning we would likely be SIGKILL'd. - """ - projected = _many_projected_mb(num_entities, rows_per_entity, num_features) - free = _free_memory_mb() - return projected < free * safety_factor, projected, free - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) # opt out of global 300s limit — large inserts take time -def test_oom_crossover(mongodb_connection_string: str, many_config: RepoConfig) -> None: - """Demonstrate that Many OOMs where One succeeds (the key trade-off). - - Both implementations run against an identical dataset. Many loads the - entire collection into memory — at large enough scale it will raise - MemoryError, which is caught and reported. One uses chunked entity-filtered - queries and therefore completes with bounded memory. - - Expected output:: - - ╔══════════════════════════════════════════════════════════════════════╗ - ║ OOM CROSSOVER DEMO — Many vs One ║ - ╠══════════════════════════════════════════════════════════════════════╣ - ║ Scale: 400,000 entities × 5 rows × 100 features ║ - ║ 2,000,000 total docs | raw data ≈ 1,600 MB ║ - ╠═══════════════════════╦════════════════╦════════════════════════════╣ - ║ Metric ║ Many ║ One ║ - ╠═══════════════════════╬════════════════╬════════════════════════════╣ - ║ Status ║ OOM ❌ ║ OK ✅ ║ - ║ Time (s) ║ -- ║ 312.4 ║ - ║ Mem tracemalloc (MB) ║ -- ║ 312.0 ║ - ║ Mem RSS Δ (MB) ║ -- ║ 189.0 ║ - ╚═══════════════════════╩════════════════╩════════════════════════════╝ - """ - num_entities = OOM_ENTITIES - num_features = OOM_FEATURES - rows_per_entity = OOM_ROWS - total_docs = num_entities * rows_per_entity - raw_mb = total_docs * num_features * 8 / 1e6 - avail_mb = _available_memory_mb() - - print( - f"\n{'=' * 72}\n" - f"OOM CROSSOVER DEMO\n" - f" Scale : {num_entities:,} entities × {rows_per_entity} rows × {num_features} features\n" - f" Docs : {total_docs:,} total | raw ≈ {raw_mb:.0f} MB\n" - f" Machine: {avail_mb / 1024:.0f} GB RAM\n" - f"{'=' * 72}" - ) - - stress_one_cfg = RepoConfig( - project="benchmark", - registry="memory://", - provider="local", - offline_store=MongoDBOfflineStoreOneConfig( - connection_string=mongodb_connection_string, - database="benchmark_db", - collection="feature_history_oom", - ), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - - # Build Many source + FV pointing at the oom_benchmark collection - many_source = MongoDBSourceMany( - name="oom_benchmark", - database="benchmark_db", - collection="oom_benchmark", - timestamp_field="event_timestamp", - ) - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - many_schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - many_fv = FeatureView( - name="oom_benchmark", - entities=[entity], - schema=many_schema, - source=many_source, - ttl=timedelta(days=1), - ) - - # Build One source + FV (collection lives in feature_history_oom) - one_source = MongoDBSourceOne( - name="oom_benchmark", timestamp_field="event_timestamp" - ) - one_schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - one_fv = FeatureView( - name="oom_benchmark", - entities=[entity], - schema=one_schema, - source=one_source, - ttl=timedelta(days=1), - ) - - feature_refs = [f"oom_benchmark:feature_{i}" for i in range(num_features)] - - client = MongoClient(mongodb_connection_string) - try: - # --- Generate data for Many --- - print(f"Generating Many data ({total_docs:,} docs)…") - now = _generate_many_data_batched( - client, - "benchmark_db", - "oom_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=rows_per_entity, - ) - - # --- Generate data for One --- - print(f"Generating One data ({total_docs:,} docs)…") - client["benchmark_db"]["feature_history_oom"].drop() - _generate_one_data_batched( - client, - "benchmark_db", - "feature_history_oom", - "oom_benchmark", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=rows_per_entity, - ) - - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - # --- Run Many --- - print("Running Many (expect OOM on machines with ≤ 32 GB RAM)…") - - def run_many(): - many_job = MongoDBOfflineStoreMany.get_historical_features( - config=many_config, - feature_views=[many_fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return many_job.to_df() - - many_result = _run_benchmark_full(run_many, mongo_client=client) - - # --- Run One --- - print("Running One (should complete within bounded memory)…") - - def run_one(): - job = MongoDBOfflineStoreOne.get_historical_features( - config=stress_one_cfg, - feature_views=[one_fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return job.to_df() - - one_result = _run_benchmark_full(run_one, mongo_client=client) - - # --- Print crossover table --- - def _fmt(result: FullBenchmarkResult, field: str) -> str: - if result.status != "OK": - return f"{result.status} ❌" - if field == "status": - return "OK ✅" - if field == "time": - return f"{result.elapsed_seconds:.1f}s" - if field == "trace": - return f"{result.peak_memory_mb:.0f} MB" - if field == "rss": - return f"{result.rss_delta_mb:.0f} MB" - return "?" - - col = 28 - print("\n" + "╔" + "═" * col + "╦" + "═" * 16 + "╦" + "═" * 16 + "╗") - print(f"║ {'OOM CROSSOVER SUMMARY':<{col - 2}} ║ {'Many':^14} ║ {'One':^14} ║") - print("╠" + "═" * col + "╬" + "═" * 16 + "╬" + "═" * 16 + "╣") - print( - f"║ {'Status':<{col - 2}} ║ {_fmt(many_result, 'status'):^14} ║ {_fmt(one_result, 'status'):^14} ║" - ) - print( - f"║ {'Time':<{col - 2}} ║ {_fmt(many_result, 'time'):^14} ║ {_fmt(one_result, 'time'):^14} ║" - ) - print( - f"║ {'Mem tracemalloc (MB)':<{col - 2}} ║ {_fmt(many_result, 'trace'):^14} ║ {_fmt(one_result, 'trace'):^14} ║" - ) - print( - f"║ {'Mem RSS Δ (MB)':<{col - 2}} ║ {_fmt(many_result, 'rss'):^14} ║ {_fmt(one_result, 'rss'):^14} ║" - ) - print("╚" + "═" * col + "╩" + "═" * 16 + "╩" + "═" * 16 + "╝") - - # One must always complete regardless of machine size - assert one_result.status == "OK", ( - f"One must complete at any scale — got {one_result.status}" - ) - - finally: - client.close() - - -# ============================================================================= -# Test 6: Realistic Benchmark — P=30 (daily batch, 30-day TTL), Single FV -# ============================================================================= -# -# Motivation: the stress tests in Test 4 used P=5 and M=50, which inflated -# memory pressure via wide documents rather than deep history. A daily batch -# feature store with a 30-day TTL is far more common — every entity has 30 -# historical rows. With M=10 features (a typical narrow feature view) and -# P=30, the OOM crossover shifts to much lower entity counts: -# -# Projected Many memory = N × 30 × 10 floats × ~130 bytes/float (empirical) -# 100 K → ~3.9 GB (comfortable) -# 300 K → ~11.7 GB (heavy) -# 600 K → ~23.4 GB (near 32 GB limit) -# 900 K → ~35.1 GB → OOM expected on 32 GB machine -# -# One memory stays bounded at CHUNK_SIZE × 10 floats × overhead ≈ 650 MB -# regardless of N. - -REALISTIC_SINGLE_ENTITY_COUNTS = [100_000, 300_000, 600_000, 900_000] -REALISTIC_NUM_FEATURES = 10 -REALISTIC_ROWS_PER_ENTITY = 30 # daily data, 30-day TTL - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) -@pytest.mark.parametrize("num_entities", REALISTIC_SINGLE_ENTITY_COUNTS) -def test_realistic_p30_single_fv_many( - mongodb_connection_string: str, many_config: RepoConfig, num_entities: int -) -> None: - """Many: realistic daily-batch scenario (M=10, P=30), single feature view. - - Collection size = N × 30 docs. Many loads the entire collection, so memory - scales linearly with N. OOM is expected around 900 K entities on a 32 GB - machine; the error is caught and reported without failing the test. - """ - num_features = REALISTIC_NUM_FEATURES - rows_per_entity = REALISTIC_ROWS_PER_ENTITY - total_docs = num_entities * rows_per_entity - - client = MongoClient(mongodb_connection_string) - try: - print( - f"\n[MANY realistic-single] Generating {total_docs:,} docs " - f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features)…" - ) - now = _generate_many_data_batched( - client, - "benchmark_db", - "realistic_single", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=rows_per_entity, - ) - - source = MongoDBSourceMany( - name="realistic_single", - database="benchmark_db", - collection="realistic_single", - timestamp_field="event_timestamp", - ) - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - fv = FeatureView( - name="realistic_single", - entities=[entity], - schema=schema, - source=source, - ttl=timedelta(days=30), - ) - feature_refs = [f"realistic_single:feature_{i}" for i in range(num_features)] - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - safe, projected_mb, free_mb = _check_memory_for_many( - num_entities, rows_per_entity, num_features - ) - if not safe: - print( - f" [MANY realistic-single] Skipping query — projected {projected_mb:,.0f} MB " - f"exceeds 80% of {free_mb:,.0f} MB free RAM. Would likely SIGKILL." - ) - result = FullBenchmarkResult( - elapsed_seconds=0, - peak_memory_mb=projected_mb, - status="SKIPPED:low_memory", - ) - else: - - def run_query(): - many_job = MongoDBOfflineStoreMany.get_historical_features( - config=many_config, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return many_job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - - _print_benchmark_result( - "MANY", - "Entities (realistic P=30, 1 FV)", - num_entities, - result, - num_entities, - ) - print( - f" Collection docs: {total_docs:,} | Raw ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" - ) - - finally: - client.close() - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) -@pytest.mark.parametrize("num_entities", REALISTIC_SINGLE_ENTITY_COUNTS) -def test_realistic_p30_single_fv_one( - mongodb_connection_string: str, num_entities: int -) -> None: - """One: realistic daily-batch scenario (M=10, P=30), single feature view. - - One's memory ceiling is O(CHUNK_SIZE × M) and does not grow with N. - All parametrize values must complete without OOM. - """ - num_features = REALISTIC_NUM_FEATURES - rows_per_entity = REALISTIC_ROWS_PER_ENTITY - total_docs = num_entities * rows_per_entity - coll = "realistic_one_single" - - client = MongoClient(mongodb_connection_string) - try: - client["benchmark_db"][coll].drop() - print( - f"\n[ONE realistic-single] Generating {total_docs:,} docs " - f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features)…" - ) - now = _generate_one_data_batched( - client, - "benchmark_db", - coll, - "realistic_single", - num_entities=num_entities, - num_features=num_features, - rows_per_entity=rows_per_entity, - ) - - one_cfg = RepoConfig( - project="benchmark", - registry="memory://", - provider="local", - offline_store=MongoDBOfflineStoreOneConfig( - connection_string=mongodb_connection_string, - database="benchmark_db", - collection=coll, - ), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - source = MongoDBSourceOne( - name="realistic_single", timestamp_field="event_timestamp" - ) - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - fv = FeatureView( - name="realistic_single", - entities=[entity], - schema=schema, - source=source, - ttl=timedelta(days=30), - ) - feature_refs = [f"realistic_single:feature_{i}" for i in range(num_features)] - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - def run_query(): - one_job = MongoDBOfflineStoreOne.get_historical_features( - config=one_cfg, - feature_views=[fv], - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return one_job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - _print_benchmark_result( - "ONE", "Entities (realistic P=30, 1 FV)", num_entities, result, num_entities - ) - print( - f" Collection docs: {total_docs:,} | Raw ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" - ) - assert result.status == "OK", f"One must never OOM — got: {result.status}" - - finally: - client.close() - - -# ============================================================================= -# Test 7: Realistic Benchmark — P=30, 3 Feature Views joined simultaneously -# ============================================================================= -# -# In production, a training job typically joins 3–5 feature views. Many loads -# every FV's collection *independently*, so memory = sum of all collections. -# With 3 FVs (M=10, P=30): -# -# Projected Many memory = 3 × N × 30 × 10 floats × ~130 bytes/float -# 50 K → 3 × ~1.95 GB = ~5.85 GB -# 100 K → 3 × ~3.9 GB = ~11.7 GB -# 200 K → 3 × ~7.8 GB = ~23.4 GB (near 32 GB limit) -# 300 K → 3 × ~11.7 GB = ~35.1 GB → OOM expected -# -# One is unaffected: it chunks each FV query independently, so memory stays -# bounded at CHUNK_SIZE × M × overhead per FV, not the product. - -REALISTIC_MULTI_ENTITY_COUNTS = [50_000, 100_000, 200_000, 300_000] -REALISTIC_NUM_FEATURE_VIEWS = 3 - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) -@pytest.mark.parametrize("num_entities", REALISTIC_MULTI_ENTITY_COUNTS) -def test_realistic_p30_multi_fv_many( - mongodb_connection_string: str, many_config: RepoConfig, num_entities: int -) -> None: - """Many: realistic multi-FV join (3 FVs, M=10, P=30). - - Many must load all 3 collections before joining — memory is additive. - OOM is expected around 200K–300K entities on a 32 GB machine. - """ - num_features = REALISTIC_NUM_FEATURES - rows_per_entity = REALISTIC_ROWS_PER_ENTITY - num_fvs = REALISTIC_NUM_FEATURE_VIEWS - total_docs = num_fvs * num_entities * rows_per_entity - - fv_names = [f"realistic_fv_{i}" for i in range(num_fvs)] - coll_names = [f"realistic_many_fv_{i}" for i in range(num_fvs)] - - client = MongoClient(mongodb_connection_string) - try: - print( - f"\n[MANY realistic-multi] Generating {total_docs:,} docs across {num_fvs} FVs " - f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features each)…" - ) - now = None - for fv_name, coll_name in zip(fv_names, coll_names): - now = _generate_many_data_batched( - client, - "benchmark_db", - coll_name, - num_entities=num_entities, - num_features=num_features, - rows_per_entity=rows_per_entity, - ) - - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - feature_views = [] - for fv_name, coll_name in zip(fv_names, coll_names): - source = MongoDBSourceMany( - name=fv_name, - database="benchmark_db", - collection=coll_name, - timestamp_field="event_timestamp", - ) - schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - feature_views.append( - FeatureView( - name=fv_name, - entities=[entity], - schema=schema, - source=source, - ttl=timedelta(days=30), - ) - ) - - feature_refs = [ - f"{fv_name}:feature_{f}" - for fv_name in fv_names - for f in range(num_features) - ] - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - # Memory check: Many loads all num_fvs collections simultaneously. - safe, projected_mb, free_mb = _check_memory_for_many( - num_entities * num_fvs, rows_per_entity, num_features - ) - if not safe: - print( - f" [MANY realistic-multi] Skipping query — projected {projected_mb:,.0f} MB " - f"exceeds 80% of {free_mb:,.0f} MB free RAM. Would likely SIGKILL." - ) - result = FullBenchmarkResult( - elapsed_seconds=0, - peak_memory_mb=projected_mb, - status="SKIPPED:low_memory", - ) - else: - - def run_query(): - many_job = MongoDBOfflineStoreMany.get_historical_features( - config=many_config, - feature_views=feature_views, - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return many_job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - - _print_benchmark_result( - "MANY", - f"Entities (realistic P=30, {num_fvs} FVs)", - num_entities, - result, - num_entities, - ) - print( - f" Total docs loaded: {total_docs:,} | Raw ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" - ) - - finally: - client.close() - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) -@pytest.mark.parametrize("num_entities", REALISTIC_MULTI_ENTITY_COUNTS) -def test_realistic_p30_multi_fv_one( - mongodb_connection_string: str, num_entities: int -) -> None: - """One: realistic multi-FV join (3 FVs, M=10, P=30). - - All 3 FVs share a single MongoDB collection, discriminated by the - ``feature_view`` field. One chunks each FV query independently — its - memory ceiling is O(CHUNK_SIZE × M) regardless of N or the number of FVs. - All parametrize values must complete without OOM. - """ - num_features = REALISTIC_NUM_FEATURES - rows_per_entity = REALISTIC_ROWS_PER_ENTITY - num_fvs = REALISTIC_NUM_FEATURE_VIEWS - total_docs = num_fvs * num_entities * rows_per_entity - coll = "realistic_one_multi" - - fv_names = [f"realistic_fv_{i}" for i in range(num_fvs)] - - client = MongoClient(mongodb_connection_string) - try: - client["benchmark_db"][coll].drop() - print( - f"\n[ONE realistic-multi] Generating {total_docs:,} docs across {num_fvs} FVs " - f"({num_entities:,} entities × {rows_per_entity} rows × {num_features} features each)…" - ) - now = None - for fv_name in fv_names: - now = _generate_one_data_batched( - client, - "benchmark_db", - coll, - fv_name, - num_entities=num_entities, - num_features=num_features, - rows_per_entity=rows_per_entity, - ) - - one_cfg = RepoConfig( - project="benchmark", - registry="memory://", - provider="local", - offline_store=MongoDBOfflineStoreOneConfig( - connection_string=mongodb_connection_string, - database="benchmark_db", - collection=coll, - ), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - feature_views = [] - for fv_name in fv_names: - source = MongoDBSourceOne(name=fv_name, timestamp_field="event_timestamp") - schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - feature_views.append( - FeatureView( - name=fv_name, - entities=[entity], - schema=schema, - source=source, - ttl=timedelta(days=30), - ) - ) - - feature_refs = [ - f"{fv_name}:feature_{f}" - for fv_name in fv_names - for f in range(num_features) - ] - entity_df = pd.DataFrame( - { - "driver_id": list(range(num_entities)), - "event_timestamp": [now] * num_entities, - } - ) - - def run_query(): - one_job = MongoDBOfflineStoreOne.get_historical_features( - config=one_cfg, - feature_views=feature_views, - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=False, - ) - return one_job.to_df() - - result = _run_benchmark_full(run_query, mongo_client=client) - _print_benchmark_result( - "ONE", - f"Entities (realistic P=30, {num_fvs} FVs)", - num_entities, - result, - num_entities, - ) - print( - f" Total docs in collection: {total_docs:,} | Raw ≈ {total_docs * num_features * 8 / 1e6:.0f} MB" - ) - assert result.status == "OK", f"One must never OOM — got: {result.status}" - - finally: - client.close() diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py deleted file mode 100644 index e8830886ef8..00000000000 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/benchmark_sweep.py +++ /dev/null @@ -1,1091 +0,0 @@ -"""MongoDB offline store benchmark suite — v2. - -Sweeps four independent dimensions across all three implementations -side-by-side in every parametrize run: - - N entity count (entity_df rows) - M feature width (features per feature view) - P observation depth (historical rows per entity in the collection) - K feature view fan-out (feature views joined simultaneously) - -Implementations ---------------- - one — single collection, Python-side PIT join (pandas merge_asof) - native — single collection, Atlas-side PIT join ($documents + $lookup) - many — per-FV collections, Ibis-based PIT join - -P is the key differentiator: native's $lookup subpipeline uses -``{$sort: {event_timestamp: -1}, $limit: 1}`` backed by the compound index, -so its cost is O(log P) per lookup. one and many must transfer all N×P -documents from MongoDB and deduplicate in Python — cost is O(N×P). - -Output ------- - stdout side-by-side table after each parametrize run - CSV benchmark_results.csv in this directory (appended, never overwritten) - -Usage ------ - Smoke (default, ~2–3 min): - pytest benchmark_v2.py -v -s - - Skip slow stress tests: - pytest benchmark_v2.py -v -s -m "not slow" - - Stress only: - pytest benchmark_v2.py -v -s -m slow - - Full sweep — edit the *_FULL constants below and swap them into the - @pytest.mark.parametrize decorators: - pytest benchmark_v2.py -v -s -""" - -from __future__ import annotations - -import csv -import gc -import resource -import sys -import time -import tracemalloc -from dataclasses import dataclass, field -from datetime import datetime, timedelta -from pathlib import Path -from typing import Any, Callable, Dict, Generator, List, Optional, Set - -import pandas as pd -import pytest -import pytz - -pytest.importorskip("pymongo") - -from unittest.mock import MagicMock - -from pymongo import MongoClient -from testcontainers.mongodb import MongoDbContainer - -from feast import Entity, FeatureView, Field -from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_agg import ( - MongoDBOfflineStoreAgg, - MongoDBOfflineStoreAggConfig, - MongoDBSourceAgg, -) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( - MongoDBOfflineStoreMany, - MongoDBOfflineStoreManyConfig, - MongoDBSourceMany, -) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( - MongoDBOfflineStoreNative, - MongoDBOfflineStoreNativeConfig, - MongoDBSourceNative, -) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( - MongoDBOfflineStoreOne, - MongoDBOfflineStoreOneConfig, - MongoDBSourceOne, -) -from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto -from feast.protos.feast.types.Value_pb2 import Value as ValueProto -from feast.repo_config import RepoConfig -from feast.types import Float64, Int64 -from feast.value_type import ValueType - -# --------------------------------------------------------------------------- -# Docker guard -# --------------------------------------------------------------------------- - -docker_available = False -try: - import docker - - try: - _docker_client = docker.from_env() - _docker_client.ping() - docker_available = True - except Exception: - pass -except ImportError: - pass - -_requires_docker = pytest.mark.skipif( - not docker_available, - reason="Docker is not available or not running.", -) - -# --------------------------------------------------------------------------- -# Constants -# --------------------------------------------------------------------------- - -ENTITY_KEY_VERSION = 3 -BENCH_DB = "bench_db" -BENCH_FH = "bench_fh" # single-collection name shared by 'one' and 'native' -_INSERT_BATCH = 50_000 # docs per insert_many call - -_CSV_PATH = Path(__file__).with_name("benchmark_results.csv") - -# --------------------------------------------------------------------------- -# Sweep ranges -# --------------------------------------------------------------------------- -# -# Smoke tier (default) — moderate sizes, safe for the Docker testcontainer. -# Each sweep holds the other three dimensions at their baseline. - -N_BASE, M_BASE, P_BASE, K_BASE = 500, 10, 5, 1 - -N_SMOKE = [200, 1_000, 4_000] # entities in entity_df -M_SMOKE = [5, 20, 50] # features per feature view -P_SMOKE = [1, 5, 20] # historical rows per entity -K_SMOKE = [1, 3] # feature views joined - -# 2×2 N×P interaction grid — reveals P-independence of 'native' -N_P_GRID_SMOKE = [(500, 5), (500, 20), (2_000, 5), (2_000, 20)] - -# Full tier — swap into the @pytest.mark.parametrize decorators for local runs. -# N_FULL = [200, 1_000, 5_000, 20_000, 100_000] -# M_FULL = [5, 20, 50, 100, 200] -# P_FULL = [1, 5, 20, 60, 120] -# K_FULL = [1, 3, 7, 15] -# N_P_GRID_FULL = [(1_000,5),(1_000,60),(10_000,5),(10_000,60)] - -# Stress tier — @pytest.mark.slow, large N. -N_STRESS = [50_000, 200_000] -M_STRESS, P_STRESS = 50, 5 - -# --------------------------------------------------------------------------- -# Entity key helper (int entity_id → serialized bytes) -# --------------------------------------------------------------------------- - - -def _make_entity_id(entity_id: int) -> bytes: - entity_key = EntityKeyProto() - entity_key.join_keys.append("driver_id") - val = ValueProto() - val.int64_val = entity_id - entity_key.entity_values.append(val) - return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) - - -# --------------------------------------------------------------------------- -# Store implementation descriptors -# --------------------------------------------------------------------------- - - -@dataclass -class StoreImpl: - """Bundles all implementation-specific factories into one object.""" - - id: str - store_class: Any - make_offline_config: Callable[[str], Any] # (conn_str) → offline store config - make_source: Callable[[str], Any] # (fv_name) → data source - - -ALL_IMPLS: List[StoreImpl] = [ - StoreImpl( - id="one", - store_class=MongoDBOfflineStoreOne, - make_offline_config=lambda conn: MongoDBOfflineStoreOneConfig( - connection_string=conn, - database=BENCH_DB, - collection=BENCH_FH, - ), - make_source=lambda fv_name: MongoDBSourceOne( - name=fv_name, - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - ), - ), - StoreImpl( - id="native", - store_class=MongoDBOfflineStoreNative, - make_offline_config=lambda conn: MongoDBOfflineStoreNativeConfig( - connection_string=conn, - database=BENCH_DB, - collection=BENCH_FH, - ), - make_source=lambda fv_name: MongoDBSourceNative( - name=fv_name, - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - ), - ), - StoreImpl( - id="many", - store_class=MongoDBOfflineStoreMany, - make_offline_config=lambda conn: MongoDBOfflineStoreManyConfig( - connection_string=conn, - database=BENCH_DB, - ), - make_source=lambda fv_name: MongoDBSourceMany( - name=fv_name, - database=BENCH_DB, - collection=fv_name, - timestamp_field="event_timestamp", - ), - ), - StoreImpl( - id="agg", - store_class=MongoDBOfflineStoreAgg, - make_offline_config=lambda conn: MongoDBOfflineStoreAggConfig( - connection_string=conn, - database=BENCH_DB, - collection=BENCH_FH, - ), - make_source=lambda fv_name: MongoDBSourceAgg( - name=fv_name, - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - ), - ), -] - -# --------------------------------------------------------------------------- -# Seed helper — writes both schemas simultaneously -# --------------------------------------------------------------------------- - - -def _seed_benchmark( - client: MongoClient, - fv_names: List[str], - num_entities: int, - num_features: int, - rows_per_entity: int, -) -> datetime: - """Insert data in both the single-collection and per-FV schemas. - - Single-collection (BENCH_FH) serves 'one' and 'native'. - Per-FV collections (one per fv_name) serve 'many'. - - Returns the ``now`` timestamp (the most recent event_timestamp seeded). - """ - import numpy as np - - db = client[BENCH_DB] - now = datetime.now(tz=pytz.UTC) - # P timestamps: now, now-1h, now-2h, …, now-(P-1)h - timestamps = [now - timedelta(hours=p) for p in range(rows_per_entity)] - feat_names = [f"feature_{f}" for f in range(num_features)] - - for fv_idx, fv_name in enumerate(fv_names): - rng = np.random.default_rng(seed=fv_idx) - feat_matrix = rng.random((num_entities * rows_per_entity, num_features)).astype( - float - ) - - single_batch: List[Dict] = [] - many_batch: List[Dict] = [] - row_idx = 0 - - for entity_id in range(num_entities): - eid_bytes = _make_entity_id(entity_id) - for p in range(rows_per_entity): - ts = timestamps[p] - feat_vals = { - feat_names[f]: float(feat_matrix[row_idx, f]) - for f in range(num_features) - } - row_idx += 1 - - # Single-collection schema (one + native) - single_batch.append( - { - "entity_id": eid_bytes, - "feature_view": fv_name, - "features": feat_vals, - "event_timestamp": ts, - "created_at": ts, - } - ) - # Per-FV schema (many) - many_batch.append( - {"driver_id": entity_id, **feat_vals, "event_timestamp": ts} - ) - - if len(single_batch) >= _INSERT_BATCH: - db[BENCH_FH].insert_many(single_batch) - db[fv_name].insert_many(many_batch) - single_batch = [] - many_batch = [] - - if single_batch: - db[BENCH_FH].insert_many(single_batch) - db[fv_name].insert_many(many_batch) - - return now - - -_COMPOUND_IDX = [ - ("entity_id", 1), - ("feature_view", 1), - ("event_timestamp", -1), - ("created_at", -1), -] - - -def _reset_and_seed( - client: MongoClient, - fv_names: List[str], - num_entities: int, - num_features: int, - rows_per_entity: int, -) -> datetime: - """Drop benchmark collections, seed fresh data, then build index. - - The compound index is created AFTER seeding so that the bulk insert is - not slowed by incremental index maintenance. All four implementations - share this index; building it once here means the first benchmark query - never has to wait for a concurrent background index build. - """ - db = client[BENCH_DB] - db[BENCH_FH].drop() - for fv_name in fv_names: - db[fv_name].drop() - now = _seed_benchmark(client, fv_names, num_entities, num_features, rows_per_entity) - # Build the compound index synchronously so queries are never forced to scan. - db[BENCH_FH].create_index(_COMPOUND_IDX, name="entity_fv_ts_idx") - return now - - -# --------------------------------------------------------------------------- -# Measurement harness -# --------------------------------------------------------------------------- - - -def _rss_mb() -> float: - """Current process peak RSS in MB (lifetime high-water-mark from the OS). - - We snapshot before/after each operation; the delta reflects the high-water - added by that operation. On macOS ru_maxrss is in bytes; on Linux, KB. - """ - rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss - return rss / (1024 * 1024) if sys.platform == "darwin" else rss / 1024 - - -@dataclass -class BenchResult: - elapsed_s: float - trace_mb: float # tracemalloc Python-heap peak for this operation - rss_mb: float = 0.0 # RSS growth attributed to this operation - mongo_ops: Dict[str, int] = field(default_factory=dict) - status: str = "OK" # "OK" | "OOM" | "SKIPPED:…" | "ERROR:…" - - -def _run_measured(func: Callable, mongo_client: Optional[Any] = None) -> BenchResult: - """Run func() and capture elapsed time, tracemalloc peak, RSS delta, Mongo ops.""" - # MongoDB opcounters snapshot - mongo_before: Optional[Dict] = None - if mongo_client: - try: - status = mongo_client.admin.command("serverStatus") - mongo_before = dict(status.get("opcounters", {})) - except Exception: - pass - - rss_before = _rss_mb() - tracemalloc.start() - t0 = time.perf_counter() - - outcome = "OK" - try: - func() - except MemoryError: - outcome = "OOM" - except Exception as exc: - outcome = f"ERROR:{str(exc)[:80]}" - - elapsed = time.perf_counter() - t0 - _, peak_bytes = tracemalloc.get_traced_memory() - tracemalloc.stop() - rss_after = _rss_mb() - - mongo_delta: Dict[str, int] = {} - if mongo_client and mongo_before: - try: - after = mongo_client.admin.command("serverStatus") - after_ops = dict(after.get("opcounters", {})) - mongo_delta = { - k: after_ops.get(k, 0) - mongo_before.get(k, 0) for k in after_ops - } - except Exception: - pass - - return BenchResult( - elapsed_s=elapsed, - trace_mb=peak_bytes / (1024 * 1024), - rss_mb=max(rss_after - rss_before, 0.0), - mongo_ops=mongo_delta, - status=outcome, - ) - - -# --------------------------------------------------------------------------- -# OOM projection utilities (carried over from benchmark.py) -# --------------------------------------------------------------------------- - -# Empirical overhead: bytes consumed by 'many' per raw float value. -# Measured in stress runs: tracemalloc peak / (N × P × M floats). -_MANY_BYTES_PER_FLOAT = 130 - - -def _projected_many_mb(N: int, P: int, M: int) -> float: - return N * P * M * _MANY_BYTES_PER_FLOAT / 1e6 - - -def _free_memory_mb() -> float: - """Estimate currently available RAM in MB (macOS vm_stat or /proc/meminfo).""" - try: - import subprocess - - out = subprocess.check_output(["vm_stat"], text=True) - page_size = 16_384 - try: - hw = subprocess.check_output( - ["sysctl", "-n", "hw.pagesize"], text=True - ).strip() - page_size = int(hw) - except Exception: - pass - free = purgeable = 0 - for line in out.splitlines(): - if line.startswith("Pages free:"): - free = int(line.split(":")[1].strip().rstrip(".")) - elif line.startswith("Pages purgeable:"): - purgeable = int(line.split(":")[1].strip().rstrip(".")) - return (free + purgeable) * page_size / (1024 * 1024) - except Exception: - pass - try: - with open("/proc/meminfo") as f: - for line in f: - if line.startswith("MemAvailable:"): - return int(line.split()[1]) / 1024 - except Exception: - pass - return 4_096 # conservative fallback - - -def _many_safe(N: int, P: int, M: int, safety: float = 0.80) -> tuple: - """Return (safe, projected_mb, free_mb).""" - proj = _projected_many_mb(N, P, M) - free = _free_memory_mb() - return proj < free * safety, proj, free - - -# --------------------------------------------------------------------------- -# FV / config factories -# --------------------------------------------------------------------------- - - -def _make_fv(impl: StoreImpl, fv_name: str, num_features: int) -> FeatureView: - entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - schema = [Field(name="driver_id", dtype=Int64)] + [ - Field(name=f"feature_{f}", dtype=Float64) for f in range(num_features) - ] - return FeatureView( - name=fv_name, - entities=[entity], - schema=schema, - source=impl.make_source(fv_name), - ttl=timedelta(days=100), # generous — never filters during benchmark - ) - - -def _make_config(impl: StoreImpl, conn_str: str) -> RepoConfig: - return RepoConfig( - project="benchmark", - registry="memory://", - provider="local", - offline_store=impl.make_offline_config(conn_str), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - - -# --------------------------------------------------------------------------- -# Run all three implementations for one scenario -# --------------------------------------------------------------------------- - - -def _run_all( - conn_str: str, - fv_names: List[str], - num_features: int, - entity_df: pd.DataFrame, - mongo_client: Any, - skip_many_if_oom: bool = False, - skip_impl_ids: Optional[Set[str]] = None, - N: int = 0, - P: int = 0, -) -> Dict[str, BenchResult]: - """Run get_historical_features on every impl and return per-impl BenchResult.""" - feature_refs = [f"{n}:feature_{f}" for n in fv_names for f in range(num_features)] - results: Dict[str, BenchResult] = {} - - for impl in ALL_IMPLS: - # Caller-supplied skip list (e.g. native at high K due to COLLSCAN × K issue) - if skip_impl_ids and impl.id in skip_impl_ids: - results[impl.id] = BenchResult( - elapsed_s=0, - trace_mb=0, - status="SKIPPED:excluded", - ) - continue - - # OOM guard for 'many' in stress scenarios. - # 'many' processes K feature views sequentially but does NOT release - # the merged result between iterations — the full K×N×M array - # accumulates. Multiply the per-FV projection by K before checking. - if skip_many_if_oom and impl.id == "many" and N > 0 and P > 0: - K = len(fv_names) - per_fv_mb = _projected_many_mb(N, P, num_features) - total_proj_mb = per_fv_mb * K - free_mb = _free_memory_mb() - safe = total_proj_mb < free_mb * 0.80 - proj_mb = total_proj_mb - if not safe: - results[impl.id] = BenchResult( - elapsed_s=0, - trace_mb=proj_mb, - status=f"SKIPPED:{proj_mb:.0f}MB_projected", - ) - print( - f" [many] SKIPPED — projected {proj_mb:.0f} MB " - f"(K={K} × {per_fv_mb:.0f} MB/FV) > 80 % of {free_mb:.0f} MB free" - ) - continue - - gc.collect() # release residual memory from previous impl - - config = _make_config(impl, conn_str) - fvs = [_make_fv(impl, n, num_features) for n in fv_names] - - def _query(impl=impl, config=config, fvs=fvs): - job = impl.store_class.get_historical_features( - config=config, - feature_views=fvs, - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="benchmark", - full_feature_names=True, - ) - return job.to_df() - - results[impl.id] = _run_measured(_query, mongo_client=mongo_client) - - return results - - -# --------------------------------------------------------------------------- -# Output helpers -# --------------------------------------------------------------------------- - -_COL_W = 13 # column width per implementation in the table - - -def _print_table(test: str, params: Dict, results: Dict[str, BenchResult]) -> None: - """Print a side-by-side results table to stdout.""" - impl_ids = list(results.keys()) - N = params.get("N", 0) - - title = ( - f"[{test}] N={params.get('N', '?')} " - f"M={params.get('M', '?')} " - f"P={params.get('P', '?')} " - f"K={params.get('K', '?')}" - ) - bar = "─" * (22 + _COL_W * len(impl_ids)) - print(f"\n{title}") - print(f" {'':22}" + "".join(f"{i:>{_COL_W}}" for i in impl_ids)) - print(f" {bar}") - - def _cell(r: Optional[BenchResult], metric: str) -> str: - if r is None: - return "—" - if r.status != "OK" and metric != "status": - return r.status[: _COL_W - 1] - if metric == "time_s": - return f"{r.elapsed_s:.3f}" - if metric == "trace_mb": - return f"{r.trace_mb:.1f}" - if metric == "rss_mb": - return f"{r.rss_mb:.1f}" - if metric == "rows_s": - return f"{N / r.elapsed_s:,.0f}" if r.elapsed_s > 0 and N > 0 else "—" - if metric == "status": - return r.status - return "—" - - for metric, label in [ - ("time_s", "time (s)"), - ("trace_mb", "trace MB"), - ("rss_mb", "RSS Δ MB"), - ("rows_s", "rows/s"), - ("status", "status"), - ]: - row = f" {label:22}" + "".join( - f"{_cell(results.get(iid), metric):>{_COL_W}}" for iid in impl_ids - ) - print(row) - - -def _append_csv(test: str, params: Dict, results: Dict[str, BenchResult]) -> None: - """Append one row per implementation to benchmark_results.csv.""" - from datetime import datetime as _dt - - ts = _dt.now().isoformat(timespec="seconds") - N = params.get("N", 0) - fieldnames = [ - "timestamp", - "test", - "impl", - "N", - "M", - "P", - "K", - "elapsed_s", - "trace_mb", - "rss_mb", - "rows_per_s", - "status", - ] - rows = [ - { - "timestamp": ts, - "test": test, - "impl": impl_id, - "N": params.get("N", ""), - "M": params.get("M", ""), - "P": params.get("P", ""), - "K": params.get("K", ""), - "elapsed_s": round(r.elapsed_s, 4), - "trace_mb": round(r.trace_mb, 2), - "rss_mb": round(r.rss_mb, 2), - "rows_per_s": round(N / r.elapsed_s, 1) if r.elapsed_s > 0 and N > 0 else 0, - "status": r.status, - } - for impl_id, r in results.items() - ] - write_header = not _CSV_PATH.exists() - with _CSV_PATH.open("a", newline="") as f: - writer = csv.DictWriter(f, fieldnames=fieldnames) - if write_header: - writer.writeheader() - writer.writerows(rows) - - -# --------------------------------------------------------------------------- -# Fixtures -# --------------------------------------------------------------------------- - - -@pytest.fixture(scope="module") -def mongodb_container() -> Generator[Optional[MongoDbContainer], None, None]: - import os - - if os.environ.get("MONGODB_URI"): - yield None # external cluster — no container needed - return - container = MongoDbContainer( - "mongo:latest", - username="test", - password="test", # pragma: allowlist secret - ).with_exposed_ports(27017) - container.start() - yield container - container.stop() - - -@pytest.fixture -def conn_str(mongodb_container: Optional[MongoDbContainer]) -> str: - import os - - uri = os.environ.get("MONGODB_URI") - if uri: - return uri - assert mongodb_container is not None - port = mongodb_container.get_exposed_port(27017) - return f"mongodb://test:test@localhost:{port}" # pragma: allowlist secret - - -# ============================================================================= -# Sweep 1: N — entity count -# -# What to look for: -# one, many : time and memory grow roughly linearly with N (they load N×P docs) -# native : time grows linearly with N (one $lookup per entity row), -# memory grows linearly with N (result is N×M features) -# but the per-entity cost should be lower than one/many because -# the PIT deduplication happens inside the $lookup on the server. -# ============================================================================= - - -@_requires_docker -@pytest.mark.parametrize("N", N_SMOKE) -def test_scale_N(conn_str: str, N: int) -> None: - """Sweep N (entities). Hold M=M_BASE, P=P_BASE, K=K_BASE.""" - M, P, K = M_BASE, P_BASE, K_BASE - fv_names = [f"fv_{k}" for k in range(K)] - params = {"N": N, "M": M, "P": P, "K": K} - - client = MongoClient(conn_str) - try: - now = _reset_and_seed(client, fv_names, N, M, P) - entity_df = pd.DataFrame( - {"driver_id": list(range(N)), "event_timestamp": [now] * N} - ) - results = _run_all(conn_str, fv_names, M, entity_df, client) - finally: - client.close() - - _print_table("scale_N", params, results) - _append_csv("scale_N", params, results) - - -# ============================================================================= -# Sweep 2: M — feature width -# -# What to look for: -# native : M affects result size only (one doc per entity row returned); -# the $lookup pipeline itself is M-independent -# one : M affects how many keys are extracted from each features subdoc -# (per-feature .apply()) — should scale gently with M -# many : M inflates every stored document; pandas DataFrame grows O(N×P×M) -# ============================================================================= - - -@_requires_docker -@pytest.mark.parametrize("M", M_SMOKE) -def test_scale_M(conn_str: str, M: int) -> None: - """Sweep M (features per FV). Hold N=N_BASE, P=P_BASE, K=K_BASE.""" - N, P, K = N_BASE, P_BASE, K_BASE - fv_names = [f"fv_{k}" for k in range(K)] - params = {"N": N, "M": M, "P": P, "K": K} - - client = MongoClient(conn_str) - try: - now = _reset_and_seed(client, fv_names, N, M, P) - entity_df = pd.DataFrame( - {"driver_id": list(range(N)), "event_timestamp": [now] * N} - ) - results = _run_all(conn_str, fv_names, M, entity_df, client) - finally: - client.close() - - _print_table("scale_M", params, results) - _append_csv("scale_M", params, results) - - -# ============================================================================= -# Sweep 3: P — observation depth (historical rows per entity) -# -# This is the most diagnostic sweep for the native implementation. -# -# What to look for: -# native : time and memory should be nearly flat as P grows — the $lookup -# subpipeline finds the single matching document via the compound -# index (entity_id, feature_view, event_timestamp DESC) and never -# materialises the other P-1 rows. Cost is O(log P) not O(P). -# one : fetches all N×P documents matching the entity_id $in list, then -# does pandas merge_asof. Both time and memory scale O(P). -# many : loads the entire collection (N×P docs) into pandas before joining. -# Both time and memory scale O(P). -# ============================================================================= - - -@_requires_docker -@pytest.mark.parametrize("P", P_SMOKE) -def test_scale_P(conn_str: str, P: int) -> None: - """Sweep P (observations per entity). Hold N=N_BASE, M=M_BASE, K=K_BASE.""" - N, M, K = N_BASE, M_BASE, K_BASE - fv_names = [f"fv_{k}" for k in range(K)] - params = {"N": N, "M": M, "P": P, "K": K} - - client = MongoClient(conn_str) - try: - now = _reset_and_seed(client, fv_names, N, M, P) - entity_df = pd.DataFrame( - {"driver_id": list(range(N)), "event_timestamp": [now] * N} - ) - results = _run_all(conn_str, fv_names, M, entity_df, client) - finally: - client.close() - - _print_table("scale_P", params, results) - _append_csv("scale_P", params, results) - - -# ============================================================================= -# Sweep 4: K — feature view fan-out -# -# What to look for: -# native : each additional FV adds one $lookup stage to the pipeline; -# the entire multi-FV join runs in a single aggregation round-trip. -# Overhead per FV should be small. -# one : one aggregation query per FV (chunked), then one merge_asof pass -# per FV. Time grows roughly linearly with K. -# many : one full collection scan per FV, then one merge_asof per FV. -# Both time and memory grow linearly with K. -# ============================================================================= - - -@_requires_docker -@pytest.mark.parametrize("K", K_SMOKE) -def test_scale_K(conn_str: str, K: int) -> None: - """Sweep K (feature views joined). Hold N=N_BASE, M=M_BASE, P=P_BASE.""" - N, M, P = N_BASE, M_BASE, P_BASE - fv_names = [f"fv_{k}" for k in range(K)] - params = {"N": N, "M": M, "P": P, "K": K} - - client = MongoClient(conn_str) - try: - now = _reset_and_seed(client, fv_names, N, M, P) - entity_df = pd.DataFrame( - {"driver_id": list(range(N)), "event_timestamp": [now] * N} - ) - results = _run_all(conn_str, fv_names, M, entity_df, client) - finally: - client.close() - - _print_table("scale_K", params, results) - _append_csv("scale_K", params, results) - - -# ============================================================================= -# Interaction: N × P -# -# Varying N and P together reveals whether native's P-independence holds across -# entity scales. If native is truly P-independent, (N=2000,P=20) should take -# roughly 4× longer than (N=500,P=20) — the same ratio as (N=2000,P=5) vs -# (N=500,P=5). For one and many the P dimension multiplies the N cost. -# -# Expected pattern (if native's index is effective): -# native time(N=500,P=20) ≈ time(N=500,P=5) ← P doesn't matter -# one time(N=500,P=20) ≈ 4× time(N=500,P=5) ← linear in P -# many time(N=500,P=20) ≈ 4× time(N=500,P=5) ← linear in P -# ============================================================================= - - -@_requires_docker -@pytest.mark.parametrize("N,P", N_P_GRID_SMOKE) -def test_interaction_N_P(conn_str: str, N: int, P: int) -> None: - """2×2 N×P grid. Hold M=M_BASE, K=K_BASE.""" - M, K = M_BASE, K_BASE - fv_names = [f"fv_{k}" for k in range(K)] - params = {"N": N, "M": M, "P": P, "K": K} - - client = MongoClient(conn_str) - try: - now = _reset_and_seed(client, fv_names, N, M, P) - entity_df = pd.DataFrame( - {"driver_id": list(range(N)), "event_timestamp": [now] * N} - ) - results = _run_all(conn_str, fv_names, M, entity_df, client) - finally: - client.close() - - _print_table("interaction_N_P", params, results) - _append_csv("interaction_N_P", params, results) - - -# ============================================================================= -# Stress: large N — memory scaling and OOM boundary -# -# At large N the memory difference between the implementations becomes stark: -# many loads N×P documents → O(N×P×M) bytes on the client. -# A MemoryError (or SKIPPED if projected to SIGKILL) is expected. -# one fetches via CHUNK_SIZE-entity batches → O(CHUNK_SIZE×P×M) peak, -# independent of total N. Must always complete. -# native fetches exactly 1 document per entity row → O(N×M) data returned, -# but the server-side $lookup + index scan is O(N) not O(N×P). -# At N=200k the $documents BSON payload (~10–15 MB) approaches -# MongoDB's 16 MB message limit; very large N may require chunking. -# ============================================================================= - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) -@pytest.mark.parametrize("N", N_STRESS) -def test_stress_scale_N(conn_str: str, N: int) -> None: - """Large-N stress test. all three implementations; OOM guard for 'many'.""" - M, P, K = M_STRESS, P_STRESS, K_BASE - fv_names = [f"fv_{k}" for k in range(K)] - params = {"N": N, "M": M, "P": P, "K": K} - total_docs = N * P - - client = MongoClient(conn_str) - try: - print( - f"\n[stress] Generating {total_docs:,} docs " - f"({N:,} entities × {P} rows × {M} features)…" - ) - now = _reset_and_seed(client, fv_names, N, M, P) - entity_df = pd.DataFrame( - {"driver_id": list(range(N)), "event_timestamp": [now] * N} - ) - results = _run_all( - conn_str, - fv_names, - M, - entity_df, - client, - skip_many_if_oom=True, - N=N, - P=P, - ) - finally: - client.close() - - _print_table("stress_scale_N", params, results) - _append_csv("stress_scale_N", params, results) - print(f" Total docs: {total_docs:,} | Raw ≈ {total_docs * M * 8 / 1e6:.0f} MB") - - assert results["one"].status == "OK", ( - f"'one' must complete at any scale via chunked fetching — got {results['one'].status}" - ) - - -# ============================================================================= -# Stress: OOM crossover — all three on the same large dataset -# -# Runs all three implementations on a single fixed scale chosen to put 'many' -# well into OOM territory on a ≤32 GB machine while 'one' and 'native' finish. -# -# Scale: OOM_N × OOM_P × OOM_M total float values in MongoDB. -# -# many projected ≈ _MANY_BYTES_PER_FLOAT × OOM_N × OOM_P × OOM_M MB -# → expected OOM or SKIPPED on ≤32 GB machines -# one CHUNK_SIZE-bounded → completes with ~several hundred MB -# native O(N×M) result → completes, but may be slow at OOM_N=200k -# ============================================================================= - -OOM_N = 200_000 -OOM_M = 100 -OOM_P = 5 - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) -def test_stress_oom_crossover(conn_str: str) -> None: - """OOM crossover demonstration — 'many' OOMs where 'one' and 'native' succeed.""" - N, M, P, K = OOM_N, OOM_M, OOM_P, K_BASE - fv_names = [f"fv_{k}" for k in range(K)] - total_docs = N * P - params = {"N": N, "M": M, "P": P, "K": K} - - print( - f"\n{'=' * 72}\n" - f"OOM CROSSOVER DEMO\n" - f" Scale : {N:,} entities × {P} rows × {M} features\n" - f" Docs : {total_docs:,} | raw ≈ {total_docs * M * 8 / 1e6:.0f} MB\n" - f" many projected peak ≈ {_projected_many_mb(N, P, M):,.0f} MB\n" - f"{'=' * 72}" - ) - - client = MongoClient(conn_str) - try: - print(f"Generating {total_docs:,} docs…") - now = _reset_and_seed(client, fv_names, N, M, P) - entity_df = pd.DataFrame( - {"driver_id": list(range(N)), "event_timestamp": [now] * N} - ) - results = _run_all( - conn_str, - fv_names, - M, - entity_df, - client, - skip_many_if_oom=True, - N=N, - P=P, - ) - finally: - client.close() - - _print_table("stress_oom_crossover", params, results) - _append_csv("stress_oom_crossover", params, results) - - assert results["one"].status == "OK", ( - f"'one' must complete regardless of scale — got {results['one'].status}" - ) - - -# ============================================================================= -# Stress: K fan-out — K-collapse comparison -# -# All K feature views share the same driver_id join key. -# -# agg : K-collapses all K FVs into ONE $match+$sort+$group aggregation, -# regardless of K. Round trips = ceil(N / MONGO_BATCH_SIZE). -# one : issues K separate $match aggregations per batch. -# Round trips = K × ceil(N / MONGO_BATCH_SIZE). -# many : does K full per-FV collection scans, one per FV. -# native : SKIPPED at all K — it issues K independent $documents+$lookup -# pipelines, each doing a full COLLSCAN of the shared collection -# (COLLSCAN cost is O(K × N × total_docs) — impractical at K ≥ 10). -# -# Expected pattern: -# agg time ≈ constant as K grows (single aggregation, K-collapse) -# one time grows O(K) (K round trips) -# many time grows O(K) (K collection scans, large network transfer) -# ============================================================================= - -N_FAN_K = 2_000 -M_FAN_K = 100 -P_FAN_K = 5 -K_FAN_VALUES = [1, 10, 100] - - -@_requires_docker -@pytest.mark.slow -@pytest.mark.timeout(0) -@pytest.mark.parametrize("K", K_FAN_VALUES) -def test_stress_fan_K(conn_str: str, K: int) -> None: - """K fan-out stress test: measures K-collapse benefit of agg vs one/many. - - native is always skipped: it issues K independent $documents+$lookup pipelines - each doing a COLLSCAN of the shared collection. At K=10 with N=2000 (100 k docs) - this is already impractical; at K=100 it would never complete. - """ - N, M, P = N_FAN_K, M_FAN_K, P_FAN_K - total_docs = N * P * K - fv_names = [f"fv_{k}" for k in range(K)] - params = {"N": N, "M": M, "P": P, "K": K} - - client = MongoClient(conn_str) - try: - print( - f"\n[stress_fan_K] Seeding {total_docs:,} docs " - f"({N:,} entities × {P} rows × {M} features × {K} FVs)…" - ) - now = _reset_and_seed(client, fv_names, N, M, P) - entity_df = pd.DataFrame( - {"driver_id": list(range(N)), "event_timestamp": [now] * N} - ) - results = _run_all( - conn_str, - fv_names, - M, - entity_df, - client, - skip_many_if_oom=True, - skip_impl_ids={"native"}, # K COLLSCANs — impractical at K >= 10 - N=N, - P=P, - ) - finally: - client.close() - - _print_table("stress_fan_K", params, results) - _append_csv("stress_fan_K", params, results) - print(f" Total docs: {total_docs:,} | Raw ≈ {total_docs * M * 8 / 1e6:.0f} MB") - - assert results["agg"].status == "OK", ( - f"'agg' must complete — got {results['agg'].status}" - ) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_cross.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_cross.py deleted file mode 100644 index e42b03beb10..00000000000 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_cross.py +++ /dev/null @@ -1,796 +0,0 @@ -"""Cross-implementation equivalence suite for MongoDB offline stores. - -Each test seeds the same logical data in **both** storage schemas, runs all -three implementations, and asserts that the feature values returned by each -are identical for the same entity/timestamp inputs. - -Storage schemas ---------------- -single-collection (serves ``one`` and ``native``): - feature_history { entity_id: bytes, feature_view: str, features: {...}, - event_timestamp: datetime, created_at: datetime } - -per-FV-collection (serves ``many``): - { : val, ..., : val, ..., - event_timestamp: datetime } - -One call to ``_seed()`` writes both schemas so all three implementations read -from their natural format without any manual duplication in test bodies. -""" - -from __future__ import annotations - -from collections import defaultdict -from dataclasses import dataclass -from datetime import datetime, timedelta -from typing import Any, Callable, Dict, List, Optional -from unittest.mock import MagicMock - -import pandas as pd -import pytest -import pytz - -pytest.importorskip("pymongo") - -from pymongo import MongoClient -from testcontainers.mongodb import MongoDbContainer - -from feast import Entity, FeatureView, Field -from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( - MongoDBOfflineStoreMany, - MongoDBOfflineStoreManyConfig, - MongoDBSourceMany, -) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( - MongoDBOfflineStoreNative, - MongoDBOfflineStoreNativeConfig, - MongoDBSourceNative, -) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( - MongoDBOfflineStoreOne, - MongoDBOfflineStoreOneConfig, - MongoDBSourceOne, -) -from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto -from feast.protos.feast.types.Value_pb2 import Value as ValueProto -from feast.repo_config import RepoConfig -from feast.types import Float64, Int64, String -from feast.value_type import ValueType - -# --------------------------------------------------------------------------- -# Docker guard -# --------------------------------------------------------------------------- - -docker_available = False -try: - import docker - - try: - _docker_client = docker.from_env() - _docker_client.ping() - docker_available = True - except Exception: - pass -except ImportError: - pass - -_requires_docker = pytest.mark.skipif( - not docker_available, - reason="Docker is not available or not running.", -) - -ENTITY_KEY_VERSION = 3 -# Separate DB so this suite never contaminates per-impl test suites -DB = "feast_cross" - - -# --------------------------------------------------------------------------- -# Entity key helper -# --------------------------------------------------------------------------- - - -def _make_entity_id( - join_keys: dict, - value_types: Optional[Dict[str, ValueType]] = None, -) -> bytes: - entity_key = EntityKeyProto() - for key in sorted(join_keys.keys()): - entity_key.join_keys.append(key) - val = ValueProto() - value = join_keys[key] - declared_type = value_types.get(key) if value_types else None - if declared_type == ValueType.INT32: - val.int32_val = int(value) - elif declared_type == ValueType.INT64: - val.int64_val = int(value) - elif declared_type == ValueType.STRING: - val.string_val = str(value) - else: - if isinstance(value, int): - val.int64_val = value - elif isinstance(value, str): - val.string_val = value - else: - val.string_val = str(value) - entity_key.entity_values.append(val) - return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) - - -# --------------------------------------------------------------------------- -# Store implementation descriptors -# --------------------------------------------------------------------------- - - -@dataclass -class StoreImpl: - """Bundles all implementation-specific factories into one object.""" - - id: str - store_class: Any - make_offline_config: Callable[[str], Any] # (conn_str) → offline store config - make_source: Callable[[str], Any] # (fv_name) → data source - - -ALL_IMPLS: List[StoreImpl] = [ - StoreImpl( - id="one", - store_class=MongoDBOfflineStoreOne, - make_offline_config=lambda conn: MongoDBOfflineStoreOneConfig( - connection_string=conn, - database=DB, - collection="feature_history", - ), - make_source=lambda fv_name: MongoDBSourceOne( - name=fv_name, - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - ), - ), - StoreImpl( - id="native", - store_class=MongoDBOfflineStoreNative, - make_offline_config=lambda conn: MongoDBOfflineStoreNativeConfig( - connection_string=conn, - database=DB, - collection="feature_history", - ), - make_source=lambda fv_name: MongoDBSourceNative( - name=fv_name, - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - ), - ), - StoreImpl( - id="many", - store_class=MongoDBOfflineStoreMany, - make_offline_config=lambda conn: MongoDBOfflineStoreManyConfig( - connection_string=conn, - database=DB, - ), - make_source=lambda fv_name: MongoDBSourceMany( - name=fv_name, - database=DB, - collection=fv_name, - timestamp_field="event_timestamp", - ), - ), -] - - -# --------------------------------------------------------------------------- -# Seed helper — writes both schemas from the same logical rows -# --------------------------------------------------------------------------- - - -def _seed(client: MongoClient, rows: List[Dict]) -> None: - """Insert rows into both storage schemas. - - Each element of *rows* is a dict with keys: - fv_name – feature view name (also the collection name for 'many') - entity_dict – {join_key: value, ...} - feature_dict – {feature_name: value, ...} - event_ts – datetime - - After this call: - - 'one' and 'native' can read from ``DB.feature_history`` - - 'many' can read from ``DB.`` - """ - db = client[DB] - - # ── Single-collection schema (one + native) ──────────────────────────── - db["feature_history"].insert_many( - [ - { - "entity_id": _make_entity_id(r["entity_dict"]), - "feature_view": r["fv_name"], - "features": r["feature_dict"], - "event_timestamp": r["event_ts"], - "created_at": r["event_ts"], - } - for r in rows - ] - ) - - # ── Per-FV-collection schema (many) ──────────────────────────────────── - by_fv: Dict[str, List[Dict]] = defaultdict(list) - for r in rows: - by_fv[r["fv_name"]].append(r) - - for fv_name, fv_rows in by_fv.items(): - db[fv_name].insert_many( - [ - { - **r["entity_dict"], - **r["feature_dict"], - "event_timestamp": r["event_ts"], - } - for r in fv_rows - ] - ) - - -# --------------------------------------------------------------------------- -# Run helper -# --------------------------------------------------------------------------- - - -def _run( - impl: StoreImpl, - conn_str: str, - fvs: List[FeatureView], - feature_refs: List[str], - entity_df: pd.DataFrame, - full_feature_names: bool = False, -) -> pd.DataFrame: - """Run get_historical_features for one implementation and return the result.""" - config = RepoConfig( - project="cross_test", - registry="memory://", - provider="local", - offline_store=impl.make_offline_config(conn_str), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - job = impl.store_class.get_historical_features( - config=config, - feature_views=fvs, - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project="cross_test", - full_feature_names=full_feature_names, - ) - return job.to_df() - - -# --------------------------------------------------------------------------- -# Assertion helper -# --------------------------------------------------------------------------- - - -def _assert_equivalence( - dfs: List[pd.DataFrame], - ids: List[str], - sort_cols: List[str], - feature_cols: List[str], -) -> None: - """Assert all DataFrames return identical feature values after sorting. - - Lenient about column order, extra columns (e.g. entity key columns that - differ between schemas), and numeric dtype coercion. NaN == NaN for - the purposes of this comparison. - """ - normed = [df.sort_values(sort_cols).reset_index(drop=True) for df in dfs] - ref, ref_id = normed[0], ids[0] - - for other, other_id in zip(normed[1:], ids[1:]): - for col in feature_cols: - pd.testing.assert_series_equal( - ref[col].reset_index(drop=True), - other[col].reset_index(drop=True), - check_names=False, - check_dtype=False, - rtol=1e-5, - obj=f"column '{col}' ({ref_id} vs {other_id})", - ) - - -# --------------------------------------------------------------------------- -# Fixtures -# --------------------------------------------------------------------------- - - -@pytest.fixture(scope="module") -def mongodb_container(): - container = MongoDbContainer( - "mongo:latest", - username="test", - password="test", # pragma: allowlist secret - ).with_exposed_ports(27017) - container.start() - yield container - container.stop() - - -@pytest.fixture -def conn_str(mongodb_container) -> str: - port = mongodb_container.get_exposed_port(27017) - return f"mongodb://test:test@localhost:{port}" # pragma: allowlist secret - - -# --------------------------------------------------------------------------- -# Tests -# --------------------------------------------------------------------------- - - -@_requires_docker -def test_pit_join_equivalence(conn_str: str) -> None: - """PIT join returns the same feature values across all three implementations. - - Uses a time-varying dataset (three rows for driver 1 at different timestamps) - and three entity rows that each point to a different historical slice. - """ - now = datetime.now(tz=pytz.UTC) - fv = "driver_pit_x" - - client = MongoClient(conn_str) - _seed( - client, - [ - { - "fv_name": fv, - "entity_dict": {"driver_id": 1}, - "feature_dict": {"conv_rate": 0.5, "acc_rate": 0.9}, - "event_ts": now - timedelta(hours=2), - }, - { - "fv_name": fv, - "entity_dict": {"driver_id": 1}, - "feature_dict": {"conv_rate": 0.6, "acc_rate": 0.85}, - "event_ts": now - timedelta(hours=1), - }, - { - "fv_name": fv, - "entity_dict": {"driver_id": 1}, - "feature_dict": {"conv_rate": 0.7, "acc_rate": 0.8}, - "event_ts": now, - }, - { - "fv_name": fv, - "entity_dict": {"driver_id": 2}, - "feature_dict": {"conv_rate": 0.3, "acc_rate": 0.95}, - "event_ts": now - timedelta(hours=2), - }, - ], - ) - client.close() - - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - entity_df = pd.DataFrame( - { - "driver_id": [1, 1, 2], - "event_timestamp": [ - now - timedelta(hours=1, minutes=30), # → 0.5 - now - timedelta(minutes=30), # → 0.6 - now - timedelta(hours=1), # → 0.3 - ], - } - ) - - dfs = [] - for impl in ALL_IMPLS: - source = impl.make_source(fv) - feature_view = FeatureView( - name=fv, - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="conv_rate", dtype=Float64), - Field(name="acc_rate", dtype=Float64), - ], - source=source, - ttl=timedelta(days=1), - ) - dfs.append( - _run( - impl, - conn_str, - [feature_view], - [f"{fv}:conv_rate", f"{fv}:acc_rate"], - entity_df, - ) - ) - - _assert_equivalence( - dfs, - [impl.id for impl in ALL_IMPLS], - sort_cols=["driver_id", "event_timestamp"], - feature_cols=["conv_rate", "acc_rate"], - ) - - -@_requires_docker -def test_ttl_equivalence(conn_str: str) -> None: - """All three implementations produce NULL for stale features and a value for fresh ones. - - Tests that TTL filtering is consistent: driver 1 (within 1-day TTL) has a - non-NULL conv_rate; driver 2 (2 days old, outside TTL) has a NULL conv_rate. - """ - now = datetime.now(tz=pytz.UTC) - fv = "driver_ttl_x" - - client = MongoClient(conn_str) - _seed( - client, - [ - { - "fv_name": fv, - "entity_dict": {"driver_id": 1}, - "feature_dict": {"conv_rate": 0.9}, - "event_ts": now - timedelta(hours=1), - }, - { - "fv_name": fv, - "entity_dict": {"driver_id": 2}, - "feature_dict": {"conv_rate": 0.5}, - "event_ts": now - timedelta(days=2), # stale - }, - ], - ) - client.close() - - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - - dfs = [] - for impl in ALL_IMPLS: - source = impl.make_source(fv) - feature_view = FeatureView( - name=fv, - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="conv_rate", dtype=Float64), - ], - source=source, - ttl=timedelta(days=1), - ) - dfs.append(_run(impl, conn_str, [feature_view], [f"{fv}:conv_rate"], entity_df)) - - # Feature values must be identical across implementations - _assert_equivalence( - dfs, - [impl.id for impl in ALL_IMPLS], - sort_cols=["driver_id"], - feature_cols=["conv_rate"], - ) - - # Verify the NULL pattern itself is consistent - for df, impl in zip(dfs, ALL_IMPLS): - normed = df.sort_values("driver_id").reset_index(drop=True) - assert not pd.isna(normed.loc[0, "conv_rate"]), ( - f"{impl.id}: driver 1 should be non-NULL (within TTL)" - ) - assert pd.isna(normed.loc[1, "conv_rate"]), ( - f"{impl.id}: driver 2 should be NULL (outside TTL)" - ) - - -@_requires_docker -def test_multi_fv_equivalence(conn_str: str) -> None: - """Features from two FVs joined on the same entity key are identical across all impls.""" - now = datetime.now(tz=pytz.UTC) - fv_driver = "driver_mfv_x" - fv_vehicle = "vehicle_mfv_x" - - client = MongoClient(conn_str) - _seed( - client, - [ - { - "fv_name": fv_driver, - "entity_dict": {"driver_id": 1}, - "feature_dict": {"rating": 4.8}, - "event_ts": now - timedelta(hours=1), - }, - { - "fv_name": fv_driver, - "entity_dict": {"driver_id": 2}, - "feature_dict": {"rating": 4.5}, - "event_ts": now - timedelta(hours=1), - }, - { - "fv_name": fv_vehicle, - "entity_dict": {"driver_id": 1}, - "feature_dict": {"vehicle_age": 2, "mileage": 50000}, - "event_ts": now - timedelta(hours=1), - }, - { - "fv_name": fv_vehicle, - "entity_dict": {"driver_id": 2}, - "feature_dict": {"vehicle_age": 5, "mileage": 120000}, - "event_ts": now - timedelta(hours=1), - }, - ], - ) - client.close() - - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - - dfs = [] - for impl in ALL_IMPLS: - fv1 = FeatureView( - name=fv_driver, - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="rating", dtype=Float64), - ], - source=impl.make_source(fv_driver), - ttl=timedelta(days=1), - ) - fv2 = FeatureView( - name=fv_vehicle, - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="vehicle_age", dtype=Int64), - Field(name="mileage", dtype=Int64), - ], - source=impl.make_source(fv_vehicle), - ttl=timedelta(days=1), - ) - dfs.append( - _run( - impl, - conn_str, - [fv1, fv2], - [ - f"{fv_driver}:rating", - f"{fv_vehicle}:vehicle_age", - f"{fv_vehicle}:mileage", - ], - entity_df, - ) - ) - - _assert_equivalence( - dfs, - [impl.id for impl in ALL_IMPLS], - sort_cols=["driver_id"], - feature_cols=["rating", "vehicle_age", "mileage"], - ) - - -@_requires_docker -def test_overlapping_feature_names_equivalence(conn_str: str) -> None: - """Three FVs sharing a feature named 'score' are handled identically with full_feature_names=True. - - This also adds overlapping-feature-name coverage to the 'many' implementation, - which lacked this test in its individual suite. - """ - now = datetime.now(tz=pytz.UTC) - fv_names = ["fv_ol_xa", "fv_ol_xb", "fv_ol_xc"] - bases = {"fv_ol_xa": 1.0, "fv_ol_xb": 2.0, "fv_ol_xc": 3.0} - - client = MongoClient(conn_str) - rows = [] - for fv_name in fv_names: - for driver_id in [1, 2]: - rows.append( - { - "fv_name": fv_name, - "entity_dict": {"driver_id": driver_id}, - "feature_dict": {"score": bases[fv_name] + driver_id * 0.1}, - "event_ts": now - timedelta(hours=1), - } - ) - _seed(client, rows) - client.close() - - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - - dfs = [] - for impl in ALL_IMPLS: - fvs = [ - FeatureView( - name=fv_name, - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="score", dtype=Float64), - ], - source=impl.make_source(fv_name), - ttl=timedelta(days=1), - ) - for fv_name in fv_names - ] - dfs.append( - _run( - impl, - conn_str, - fvs, - [f"{n}:score" for n in fv_names], - entity_df, - full_feature_names=True, - ) - ) - - prefixed_cols = [f"{n}__score" for n in fv_names] - _assert_equivalence( - dfs, - [impl.id for impl in ALL_IMPLS], - sort_cols=["driver_id"], - feature_cols=prefixed_cols, - ) - - -@_requires_docker -def test_compound_keys_equivalence(conn_str: str) -> None: - """Compound join keys (user_id, device_id) produce identical results across all impls.""" - now = datetime.now(tz=pytz.UTC) - fv = "user_device_x" - - client = MongoClient(conn_str) - _seed( - client, - [ - { - "fv_name": fv, - "entity_dict": {"user_id": 1, "device_id": "mobile"}, - "feature_dict": {"app_opens": 55}, - "event_ts": now - timedelta(hours=1), - }, - { - "fv_name": fv, - "entity_dict": {"user_id": 1, "device_id": "desktop"}, - "feature_dict": {"app_opens": 10}, - "event_ts": now - timedelta(hours=1), - }, - { - "fv_name": fv, - "entity_dict": {"user_id": 2, "device_id": "tablet"}, - "feature_dict": {"app_opens": 25}, - "event_ts": now - timedelta(hours=1), - }, - ], - ) - client.close() - - user_entity = Entity( - name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 - ) - device_entity = Entity( - name="device_id", join_keys=["device_id"], value_type=ValueType.STRING - ) - entity_df = pd.DataFrame( - { - "user_id": [1, 1, 2], - "device_id": ["mobile", "desktop", "tablet"], - "event_timestamp": [now, now, now], - } - ) - - dfs = [] - for impl in ALL_IMPLS: - feature_view = FeatureView( - name=fv, - entities=[user_entity, device_entity], - schema=[ - Field(name="user_id", dtype=Int64), - Field(name="device_id", dtype=String), - Field(name="app_opens", dtype=Int64), - ], - source=impl.make_source(fv), - ttl=timedelta(days=1), - ) - dfs.append(_run(impl, conn_str, [feature_view], [f"{fv}:app_opens"], entity_df)) - - _assert_equivalence( - dfs, - [impl.id for impl in ALL_IMPLS], - sort_cols=["user_id", "device_id"], - feature_cols=["app_opens"], - ) - - -@_requires_docker -def test_extra_columns_equivalence(conn_str: str) -> None: - """Extra label columns in entity_df pass through unchanged and do not affect features. - - This is the cross-implementation regression test for the union-key bug: if any - implementation folds non-join-key columns into the entity key, features come - back NULL. Here we verify that all three return the same non-NULL feature - values and preserve the label column. - """ - now = datetime.now(tz=pytz.UTC) - fv = "driver_extra_x" - - client = MongoClient(conn_str) - _seed( - client, - [ - { - "fv_name": fv, - "entity_dict": {"driver_id": 1}, - "feature_dict": {"conv_rate": 0.5}, - "event_ts": now - timedelta(hours=2), - }, - { - "fv_name": fv, - "entity_dict": {"driver_id": 1}, - "feature_dict": {"conv_rate": 0.6}, - "event_ts": now - timedelta(hours=1), - }, - { - "fv_name": fv, - "entity_dict": {"driver_id": 2}, - "feature_dict": {"conv_rate": 0.3}, - "event_ts": now - timedelta(hours=2), - }, - ], - ) - client.close() - - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - entity_df = pd.DataFrame( - { - "driver_id": [1, 1, 2], - "event_timestamp": [ - now - timedelta(hours=1, minutes=30), - now - timedelta(minutes=30), - now - timedelta(hours=1), - ], - "trip_success": [1, 0, 1], # label column — must not enter entity key - } - ) - - dfs = [] - for impl in ALL_IMPLS: - source = impl.make_source(fv) - feature_view = FeatureView( - name=fv, - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="conv_rate", dtype=Float64), - ], - source=source, - ttl=timedelta(days=1), - ) - dfs.append(_run(impl, conn_str, [feature_view], [f"{fv}:conv_rate"], entity_df)) - - # Feature values must be identical - _assert_equivalence( - dfs, - [impl.id for impl in ALL_IMPLS], - sort_cols=["driver_id", "event_timestamp"], - feature_cols=["conv_rate"], - ) - - # Features must be non-NULL (union-key bug would produce all-NULL here) - for df, impl in zip(dfs, ALL_IMPLS): - assert df["conv_rate"].notna().all(), ( - f"{impl.id}: conv_rate is null — label column may have been " - "folded into the entity key serialization." - ) - - # Label column must be present and unchanged in all three - for df, impl in zip(dfs, ALL_IMPLS): - assert "trip_success" in df.columns, f"{impl.id}: trip_success column missing" - assert sorted(df["trip_success"].tolist()) == [0, 1, 1], ( - f"{impl.id}: trip_success values changed" - ) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py deleted file mode 100644 index 71e764dae43..00000000000 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_many.py +++ /dev/null @@ -1,778 +0,0 @@ -""" -Unit tests for MongoDB offline store (Ibis-based implementation). - -Docker-dependent tests are marked with ``@_requires_docker`` and are skipped when -Docker is unavailable. -""" - -from datetime import datetime, timedelta -from typing import Generator -from unittest.mock import MagicMock - -import pandas as pd -import pytest -import pytz - -pytest.importorskip("pymongo") - -from pymongo import MongoClient -from testcontainers.mongodb import MongoDbContainer - -from feast import Entity, FeatureView, Field -from feast.errors import SavedDatasetLocationAlreadyExists -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( - MongoDBOfflineStoreMany, - MongoDBOfflineStoreManyConfig, - MongoDBSourceMany, - SavedDatasetMongoDBStorageMany, -) -from feast.repo_config import RepoConfig -from feast.types import Float64, Int64, String -from feast.value_type import ValueType - -# Check if Docker is available -docker_available = False -try: - import docker - - try: - client = docker.from_env() - client.ping() - docker_available = True - except Exception: - pass -except ImportError: - pass - -_requires_docker = pytest.mark.skipif( - not docker_available, - reason="Docker is not available or not running.", -) - - -@pytest.fixture(scope="module") -def mongodb_container() -> Generator[MongoDbContainer, None, None]: - """Start a MongoDB container for testing.""" - container = MongoDbContainer( - "mongo:latest", - username="test", - password="test", # pragma: allowlist secret - ).with_exposed_ports(27017) - container.start() - yield container - container.stop() - - -@pytest.fixture -def mongodb_connection_string(mongodb_container: MongoDbContainer) -> str: - """Get MongoDB connection string from the container.""" - exposed_port = mongodb_container.get_exposed_port(27017) - return f"mongodb://test:test@localhost:{exposed_port}" # pragma: allowlist secret - - -@pytest.fixture -def repo_config(mongodb_connection_string: str) -> RepoConfig: - """Create a RepoConfig with MongoDB offline store.""" - return RepoConfig( - project="test_project", - registry="memory://", - provider="local", - offline_store=MongoDBOfflineStoreManyConfig( - connection_string=mongodb_connection_string, - database="feast_test", - ), - online_store={"type": "sqlite"}, - entity_key_serialization_version=3, - ) - - -@pytest.fixture -def sample_data(mongodb_connection_string: str) -> datetime: - """Insert sample driver stats data into MongoDB. - - Returns the 'now' timestamp used as the latest event_timestamp. - - Note: The collection name 'driver_stats' is defined in the MongoDBSource - (see driver_source fixture), not in the RepoConfig. RepoConfig provides - connection_string and database; the source defines the collection. - """ - client: MongoClient = MongoClient(mongodb_connection_string) - db = client["feast_test"] - collection = db["driver_stats"] - collection.drop() - - now = datetime.now(tz=pytz.UTC) - docs = [ - { - "driver_id": 1, - "conv_rate": 0.5, - "acc_rate": 0.9, - "event_timestamp": now - timedelta(hours=2), - }, - { - "driver_id": 1, - "conv_rate": 0.6, - "acc_rate": 0.85, - "event_timestamp": now - timedelta(hours=1), - }, - {"driver_id": 1, "conv_rate": 0.7, "acc_rate": 0.8, "event_timestamp": now}, - { - "driver_id": 2, - "conv_rate": 0.3, - "acc_rate": 0.95, - "event_timestamp": now - timedelta(hours=2), - }, - # Driver 2 has no "now" timestamp - only data from 2 hours ago - # This tests that pull_latest correctly handles entities with different latest timestamps - ] - collection.insert_many(docs) - client.close() - return now - - -@pytest.fixture -def driver_source() -> MongoDBSourceMany: - """Create a MongoDBSourceMany for driver stats.""" - return MongoDBSourceMany( - name="driver_stats", - database="feast_test", - collection="driver_stats", - timestamp_field="event_timestamp", - ) - - -@pytest.fixture -def driver_fv(driver_source: MongoDBSourceMany) -> FeatureView: - """Create a FeatureView for driver stats. - - The ttl (time-to-live) parameter defines how far back in time Feast will look - for feature values during point-in-time joins. If a feature's event_timestamp - is older than (entity_timestamp - ttl), that feature value is considered stale - and will be returned as NULL. - - This is different from MongoDB TTL indexes which automatically delete documents - after a period of time. Feast TTL is a query-time filter, not a storage policy. - """ - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - return FeatureView( - name="driver_stats", - entities=[driver_entity], - schema=[ - # Include entity column in schema so entity_columns is populated - Field(name="driver_id", dtype=Int64), - Field(name="conv_rate", dtype=Float64), - Field(name="acc_rate", dtype=Float64), - ], - source=driver_source, - ttl=timedelta(days=1), - ) - - -@_requires_docker -def test_pull_latest_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceMany -) -> None: - """Test pulling latest features per entity from MongoDB. - - This test verifies that pull_latest returns only the most recent feature - values for each entity (driver_id), even when entities have different - latest timestamps. Driver 1 has data at now, but driver 2's latest data - is from 2 hours ago. - """ - now = sample_data - job = MongoDBOfflineStoreMany.pull_latest_from_table_or_query( - config=repo_config, - data_source=driver_source, - join_key_columns=["driver_id"], - feature_name_columns=["conv_rate", "acc_rate"], - timestamp_field="event_timestamp", - created_timestamp_column=None, - start_date=now - timedelta(days=1), - end_date=now + timedelta(hours=1), - ) - - df = job.to_df() - - # Validate DataFrame structure - assert isinstance(df, pd.DataFrame) - assert set(df.columns) == {"driver_id", "conv_rate", "acc_rate", "event_timestamp"} - assert len(df) == 2 # Two unique drivers - - # Extract rows for each driver - driver1_rows = df[df["driver_id"] == 1] - driver2_rows = df[df["driver_id"] == 2] - - # Each driver should have exactly one row (the latest) - assert len(driver1_rows) == 1 - assert len(driver2_rows) == 1 - - driver1 = driver1_rows.iloc[0] - driver2 = driver2_rows.iloc[0] - - # Validate types - assert isinstance(driver1["conv_rate"], float) - assert isinstance(driver1["acc_rate"], float) - - # Driver 1's latest values (from "now") - assert driver1["conv_rate"] == pytest.approx(0.7) - assert driver1["acc_rate"] == pytest.approx(0.8) - - # Driver 2's latest values (from 2 hours ago - driver 2 has no "now" data) - # This demonstrates that pull_latest correctly handles entities with - # different "latest" timestamps - assert driver2["conv_rate"] == pytest.approx(0.3) - assert driver2["acc_rate"] == pytest.approx(0.95) - - -@_requires_docker -def test_get_historical_features_pit_join( - repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView -) -> None: - """Test point-in-time join retrieves correct feature values. - - Point-in-time (PIT) join ensures that for each entity row, we get the - feature values that were valid AT THAT POINT IN TIME - not future data - that would cause data leakage in ML training. - """ - now = sample_data - - # Entity dataframe: request features at specific timestamps - # Each row says "give me driver X's features as they were at time T" - entity_df = pd.DataFrame( - { - "driver_id": [1, 1, 2], - "event_timestamp": [ - now - - timedelta( - hours=1, minutes=30 - ), # Should get conv_rate=0.5 (before 0.6 was written) - now - - timedelta( - minutes=30 - ), # Should get conv_rate=0.6 (before 0.7 was written) - now - - timedelta(hours=1), # Should get conv_rate=0.3 (only data available) - ], - } - ) - - job = MongoDBOfflineStoreMany.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df() - assert isinstance(result_df, pd.DataFrame) - assert len(result_df) == 3 - - # Sort by driver_id and event_timestamp for predictable assertions - result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( - drop=True - ) - - # Driver 1, first request (1.5 hours ago) → should get value from 2 hours ago - assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) - - # Driver 1, second request (30 min ago) → should get value from 1 hour ago - assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) - - # Driver 2, request (1 hour ago) → should get value from 2 hours ago - assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) - - -@_requires_docker -def test_pull_all_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceMany -) -> None: - """Test pulling all features within a time range (no deduplication).""" - now = sample_data - job = MongoDBOfflineStoreMany.pull_all_from_table_or_query( - config=repo_config, - data_source=driver_source, - join_key_columns=["driver_id"], - feature_name_columns=["conv_rate", "acc_rate"], - timestamp_field="event_timestamp", - created_timestamp_column=None, - start_date=now - timedelta(hours=1, minutes=30), - end_date=now + timedelta(hours=1), - ) - - df = job.to_df() - assert isinstance(df, pd.DataFrame) - # Should get 2 rows: driver 1 (1hr ago, now) - # Excludes: driver 1 row from 2 hours ago (before start_date) - # driver 2 row from 2 hours ago (before start_date) - assert len(df) == 2 - - -@_requires_docker -def test_ttl_excludes_stale_features( - repo_config: RepoConfig, - mongodb_connection_string: str, - driver_source: MongoDBSourceMany, -) -> None: - """Test that TTL causes stale feature values to be returned as NULL. - - Feast TTL (time-to-live) is a query-time filter: if a feature's event_timestamp - is older than (entity_timestamp - ttl), that feature is considered stale. - This is different from MongoDB TTL indexes which delete documents. - """ - # Insert data with a very old timestamp - client: MongoClient = MongoClient(mongodb_connection_string) - db = client["feast_test"] - collection = db["driver_stats_ttl_test"] - collection.drop() - - now = datetime.now(tz=pytz.UTC) - docs = [ - # Fresh data (within TTL) - {"driver_id": 1, "conv_rate": 0.9, "event_timestamp": now - timedelta(hours=1)}, - # Stale data (outside 1-day TTL when queried from "now") - {"driver_id": 2, "conv_rate": 0.5, "event_timestamp": now - timedelta(days=2)}, - ] - collection.insert_many(docs) - client.close() - - # Create source and feature view with 1-day TTL - ttl_source = MongoDBSourceMany( - name="driver_stats_ttl_test", - database="feast_test", - collection="driver_stats_ttl_test", - timestamp_field="event_timestamp", - ) - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - ttl_fv = FeatureView( - name="driver_stats_ttl_test", - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="conv_rate", dtype=Float64), - ], - source=ttl_source, - ttl=timedelta(days=1), # Features older than 1 day are stale - ) - - # Request features "as of now" for both drivers - entity_df = pd.DataFrame( - { - "driver_id": [1, 2], - "event_timestamp": [now, now], - } - ) - - job = MongoDBOfflineStoreMany.get_historical_features( - config=repo_config, - feature_views=[ttl_fv], - feature_refs=["driver_stats_ttl_test:conv_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) - - # Driver 1: fresh data within TTL → should have value - assert result_df.loc[0, "conv_rate"] == pytest.approx(0.9) - - # Driver 2: stale data outside TTL → should be NULL - assert pd.isna(result_df.loc[1, "conv_rate"]) - - -@_requires_docker -def test_multiple_feature_views( - repo_config: RepoConfig, mongodb_connection_string: str -) -> None: - """Test joining features from multiple MongoDB collections/FeatureViews. - - This simulates a real-world scenario where features come from different - data sources (e.g., driver stats from one collection, vehicle stats from another). - """ - client: MongoClient = MongoClient(mongodb_connection_string) - db = client["feast_test"] - - # Collection 1: Driver stats - driver_collection = db["driver_stats_multi"] - driver_collection.drop() - now = datetime.now(tz=pytz.UTC) - driver_docs = [ - {"driver_id": 1, "rating": 4.8, "event_timestamp": now - timedelta(hours=1)}, - {"driver_id": 2, "rating": 4.5, "event_timestamp": now - timedelta(hours=1)}, - ] - driver_collection.insert_many(driver_docs) - - # Collection 2: Vehicle stats (same driver_id, different features) - vehicle_collection = db["vehicle_stats_multi"] - vehicle_collection.drop() - vehicle_docs = [ - { - "driver_id": 1, - "vehicle_age": 2, - "mileage": 50000, - "event_timestamp": now - timedelta(hours=1), - }, - { - "driver_id": 2, - "vehicle_age": 5, - "mileage": 120000, - "event_timestamp": now - timedelta(hours=1), - }, - ] - vehicle_collection.insert_many(vehicle_docs) - client.close() - - # Create sources for each collection - driver_source = MongoDBSourceMany( - name="driver_stats_multi", - database="feast_test", - collection="driver_stats_multi", - timestamp_field="event_timestamp", - ) - vehicle_source = MongoDBSourceMany( - name="vehicle_stats_multi", - database="feast_test", - collection="vehicle_stats_multi", - timestamp_field="event_timestamp", - ) - - # Create entities and feature views - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - - driver_fv = FeatureView( - name="driver_stats_multi", - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="rating", dtype=Float64), - ], - source=driver_source, - ttl=timedelta(days=1), - ) - - vehicle_fv = FeatureView( - name="vehicle_stats_multi", - entities=[ - driver_entity - ], # todo these two FeatureViews have the same entities list [driver_entity] - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="vehicle_age", dtype=Int64), - Field(name="mileage", dtype=Int64), - ], - source=vehicle_source, - ttl=timedelta(days=1), - ) - - # Entity dataframe requesting features for both drivers - entity_df = pd.DataFrame( - { - "driver_id": [1, 2], - "event_timestamp": [now, now], - } - ) - - # Request features from BOTH feature views - job = MongoDBOfflineStoreMany.get_historical_features( - config=repo_config, - feature_views=[driver_fv, vehicle_fv], - feature_refs=[ - "driver_stats_multi:rating", - "vehicle_stats_multi:vehicle_age", - "vehicle_stats_multi:mileage", - ], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) - - # Verify we got features from both collections joined correctly - assert len(result_df) == 2 - assert set(result_df.columns) >= {"driver_id", "rating", "vehicle_age", "mileage"} - - # Driver 1 - assert result_df.loc[0, "rating"] == pytest.approx(4.8) - assert result_df.loc[0, "vehicle_age"] == 2 - assert result_df.loc[0, "mileage"] == 50000 - - # Driver 2 - assert result_df.loc[1, "rating"] == pytest.approx(4.5) - assert result_df.loc[1, "vehicle_age"] == 5 - assert result_df.loc[1, "mileage"] == 120000 - - -@_requires_docker -def test_compound_join_keys( - repo_config: RepoConfig, mongodb_connection_string: str -) -> None: - """Test with compound/composite join keys (multiple entity columns). - - This tests scenarios where entities are identified by multiple keys, - e.g., (user_id, device_id) or (store_id, product_id). - """ - client: MongoClient = MongoClient(mongodb_connection_string) - db = client["feast_test"] - - # Create collection with compound key (user_id + device_id) - collection = db["user_device_features"] - collection.drop() - now = datetime.now(tz=pytz.UTC) - - # Same user_id can have different device_ids with different features - docs = [ - { - "user_id": 1, - "device_id": "mobile", - "app_opens": 50, - "event_timestamp": now - timedelta(hours=2), - }, - { - "user_id": 1, - "device_id": "mobile", - "app_opens": 55, - "event_timestamp": now - timedelta(hours=1), - }, - { - "user_id": 1, - "device_id": "desktop", - "app_opens": 10, - "event_timestamp": now - timedelta(hours=1), - }, - { - "user_id": 2, - "device_id": "mobile", - "app_opens": 100, - "event_timestamp": now - timedelta(hours=1), - }, - { - "user_id": 2, - "device_id": "tablet", - "app_opens": 25, - "event_timestamp": now - timedelta(hours=1), - }, - ] - collection.insert_many(docs) - client.close() - - # Create source - source = MongoDBSourceMany( - name="user_device_features", - database="feast_test", - collection="user_device_features", - timestamp_field="event_timestamp", - ) - - # Create entities with compound keys - user_entity = Entity( - name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 - ) - device_entity = Entity( - name="device_id", join_keys=["device_id"], value_type=ValueType.STRING - ) - - fv = FeatureView( - name="user_device_features", - entities=[user_entity, device_entity], - schema=[ - Field(name="user_id", dtype=Int64), - Field(name="device_id", dtype=String), - Field(name="app_opens", dtype=Int64), - ], - source=source, - ttl=timedelta(days=1), - ) - - # Test pull_latest: should get one row per unique (user_id, device_id) combination - job = MongoDBOfflineStoreMany.pull_latest_from_table_or_query( - config=repo_config, - data_source=source, - join_key_columns=["user_id", "device_id"], - feature_name_columns=["app_opens"], - timestamp_field="event_timestamp", - created_timestamp_column=None, - start_date=now - timedelta(days=1), - end_date=now + timedelta(hours=1), - ) - - df = job.to_df() - assert len(df) == 4 # 4 unique (user_id, device_id) combinations - - # Verify user 1, mobile got the LATEST value (55, not 50) - user1_mobile = df[(df["user_id"] == 1) & (df["device_id"] == "mobile")] - assert len(user1_mobile) == 1 - assert user1_mobile.iloc[0]["app_opens"] == 55 - - # Test get_historical_features with compound keys - entity_df = pd.DataFrame( - { - "user_id": [1, 1, 2], - "device_id": ["mobile", "desktop", "tablet"], - "event_timestamp": [now, now, now], - } - ) - - job = MongoDBOfflineStoreMany.get_historical_features( - config=repo_config, - feature_views=[fv], - feature_refs=["user_device_features:app_opens"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df() - assert len(result_df) == 3 - - # Sort for predictable assertions - result_df = result_df.sort_values(["user_id", "device_id"]).reset_index(drop=True) - - # user 1, desktop - assert result_df.loc[0, "app_opens"] == 10 - # user 1, mobile (latest value) - assert result_df.loc[1, "app_opens"] == 55 - # user 2, tablet - assert result_df.loc[2, "app_opens"] == 25 - - -# ============================================================================= -# persist() tests -# ============================================================================= - - -@_requires_docker -def test_persist_writes_flat_result( - repo_config: RepoConfig, - sample_data: datetime, - driver_fv: FeatureView, - mongodb_connection_string: str, -) -> None: - """persist() writes the flat joined DataFrame to the named destination collection.""" - now = sample_data - dest = "saved_ds_many_basic" - - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client.close() - - entity_df = pd.DataFrame( - { - "driver_id": [1, 2], - "event_timestamp": [now, now - timedelta(hours=2)], - } - ) - - job = MongoDBOfflineStoreMany.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - job.persist(SavedDatasetMongoDBStorageMany(database="feast_test", collection=dest)) - - client = MongoClient(mongodb_connection_string) - try: - docs = list(client["feast_test"][dest].find({}, {"_id": 0})) - finally: - client.close() - - assert len(docs) == 2 - assert {"driver_id", "conv_rate", "acc_rate", "event_timestamp"}.issubset( - docs[0].keys() - ) - - -@_requires_docker -def test_persist_raises_if_collection_exists( - repo_config: RepoConfig, - sample_data: datetime, - driver_fv: FeatureView, - mongodb_connection_string: str, -) -> None: - """persist() raises SavedDatasetLocationAlreadyExists when the destination is non-empty.""" - now = sample_data - dest = "saved_ds_many_no_overwrite" - - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client["feast_test"][dest].insert_one({"placeholder": True}) - client.close() - - entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) - - job = MongoDBOfflineStoreMany.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - with pytest.raises(SavedDatasetLocationAlreadyExists): - job.persist( - SavedDatasetMongoDBStorageMany(database="feast_test", collection=dest), - allow_overwrite=False, - ) - - -@_requires_docker -def test_persist_allow_overwrite( - repo_config: RepoConfig, - sample_data: datetime, - driver_fv: FeatureView, - mongodb_connection_string: str, -) -> None: - """persist(allow_overwrite=True) replaces any existing collection contents.""" - now = sample_data - dest = "saved_ds_many_overwrite" - - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client["feast_test"][dest].insert_many([{"stale": True}, {"stale": True}]) - client.close() - - entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) - - job = MongoDBOfflineStoreMany.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - job.persist( - SavedDatasetMongoDBStorageMany(database="feast_test", collection=dest), - allow_overwrite=True, - ) - - client = MongoClient(mongodb_connection_string) - try: - docs = list(client["feast_test"][dest].find({}, {"_id": 0})) - finally: - client.close() - - assert len(docs) == 1 - assert "stale" not in docs[0] - assert "driver_id" in docs[0] diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py similarity index 53% rename from sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py rename to sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py index 2dcdca06737..79a306f8e1e 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py @@ -1,25 +1,28 @@ """ -Unit tests for MongoDB Native offline store implementation. +Unit tests for MongoDB offline store (mongodb.py). -This tests the single-collection schema where all feature views share one -collection (``feature_history``), discriminated by ``feature_view`` field. +Tests the single-collection schema with grouped aggregation. All feature +views share one collection (``feature_history``) discriminated by the +``feature_view`` field. Schema: { - "entity_id": bytes, # serialized entity key - "feature_view": str, - "features": { "feat1": val, ... }, + "entity_id": bytes, # serialized entity key + "feature_view": str, # discriminator + "features": {"feat1": val, ...}, "event_timestamp": datetime, - "created_at": datetime + "created_at": datetime } Docker-dependent tests are marked with ``@_requires_docker`` and are skipped when Docker is unavailable. """ +import os +import tempfile from datetime import datetime, timedelta from typing import Dict, Generator, Optional -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import pandas as pd import pyarrow @@ -34,26 +37,29 @@ from feast import Entity, FeatureView, Field from feast.errors import SavedDatasetLocationAlreadyExists from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( - MongoDBOfflineStoreOne, - MongoDBOfflineStoreOneConfig, - MongoDBSourceOne, - SavedDatasetMongoDBStorageOne, +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb import ( + MongoDBOfflineStore, + MongoDBOfflineStoreConfig, + MongoDBSource, ) +from feast.infra.offline_stores.file_source import SavedDatasetFileStorage from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto from feast.repo_config import RepoConfig from feast.types import Float64, Int32, Int64, String from feast.value_type import ValueType -# Check if Docker is available +# --------------------------------------------------------------------------- +# Docker availability check +# --------------------------------------------------------------------------- + docker_available = False try: import docker try: - client = docker.from_env() - client.ping() + _docker_client = docker.from_env() + _docker_client.ping() docker_available = True except Exception: pass @@ -68,19 +74,16 @@ ENTITY_KEY_VERSION = 3 +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + def _make_entity_id( join_keys: dict, value_types: Optional[Dict[str, ValueType]] = None, ) -> bytes: - """Create serialized entity key from join key dict. - - Args: - join_keys: Mapping of join key name → value. - value_types: Optional declared ValueType per key. When provided the - correct proto field is used (e.g. INT32 → int32_val), matching what - _serialize_entity_key_from_row produces when the FeatureView declares - that type. Omit to get the default int64_val/string_val inference. - """ + """Serialize an entity key dict to bytes (matches offline_write_batch serialization).""" entity_key = EntityKeyProto() for key in sorted(join_keys.keys()): entity_key.join_keys.append(key) @@ -93,10 +96,6 @@ def _make_entity_id( val.int64_val = int(value) elif declared_type == ValueType.STRING: val.string_val = str(value) - elif declared_type == ValueType.BYTES: - val.bytes_val = bytes(value) - elif declared_type == ValueType.UNIX_TIMESTAMP: - val.unix_timestamp_val = int(value) else: if isinstance(value, bool): val.bool_val = value @@ -110,9 +109,13 @@ def _make_entity_id( return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + @pytest.fixture(scope="module") def mongodb_container() -> Generator[MongoDbContainer, None, None]: - """Start a MongoDB container for testing.""" container = MongoDbContainer( "mongo:latest", username="test", @@ -125,19 +128,17 @@ def mongodb_container() -> Generator[MongoDbContainer, None, None]: @pytest.fixture def mongodb_connection_string(mongodb_container: MongoDbContainer) -> str: - """Get MongoDB connection string from the container.""" exposed_port = mongodb_container.get_exposed_port(27017) return f"mongodb://test:test@localhost:{exposed_port}" # pragma: allowlist secret @pytest.fixture def repo_config(mongodb_connection_string: str) -> RepoConfig: - """Create a RepoConfig with MongoDB Native offline store.""" return RepoConfig( project="test_project", registry="memory://", provider="local", - offline_store=MongoDBOfflineStoreOneConfig( + offline_store=MongoDBOfflineStoreConfig( connection_string=mongodb_connection_string, database="feast_test", collection="feature_history", @@ -149,10 +150,9 @@ def repo_config(mongodb_connection_string: str) -> RepoConfig: @pytest.fixture def sample_data(mongodb_connection_string: str) -> datetime: - """Insert sample data using the single-collection schema. + """Insert sample data using the feature_history schema. - Creates documents for 'driver_stats' feature view with entity_id, - feature_view discriminator, and nested features subdocument. + 4 documents: driver 1 at 3 timestamps, driver 2 at 1 timestamp. """ client: MongoClient = MongoClient(mongodb_connection_string) db = client["feast_test"] @@ -161,7 +161,6 @@ def sample_data(mongodb_connection_string: str) -> datetime: now = datetime.now(tz=pytz.UTC) - # Create documents using the native schema docs = [ { "entity_id": _make_entity_id({"driver_id": 1}), @@ -198,9 +197,8 @@ def sample_data(mongodb_connection_string: str) -> datetime: @pytest.fixture -def driver_source() -> MongoDBSourceOne: - """Create a MongoDBSourceOne for driver stats.""" - return MongoDBSourceOne( +def driver_source() -> MongoDBSource: + return MongoDBSource( name="driver_stats", timestamp_field="event_timestamp", created_timestamp_column="created_at", @@ -208,8 +206,7 @@ def driver_source() -> MongoDBSourceOne: @pytest.fixture -def driver_fv(driver_source: MongoDBSourceOne) -> FeatureView: - """Create a FeatureView for driver stats.""" +def driver_fv(driver_source: MongoDBSource) -> FeatureView: driver_entity = Entity( name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 ) @@ -226,13 +223,18 @@ def driver_fv(driver_source: MongoDBSourceOne) -> FeatureView: ) +# --------------------------------------------------------------------------- +# Tests: pull_latest_from_table_or_query +# --------------------------------------------------------------------------- + + @_requires_docker def test_pull_latest_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceOne + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSource ) -> None: - """Test pulling latest features per entity from the single collection.""" + """pull_latest returns the most recent row per entity within the time window.""" now = sample_data - job = MongoDBOfflineStoreOne.pull_latest_from_table_or_query( + job = MongoDBOfflineStore.pull_latest_from_table_or_query( config=repo_config, data_source=driver_source, join_key_columns=["driver_id"], @@ -246,35 +248,37 @@ def test_pull_latest_from_table_or_query( df = job.to_df() assert isinstance(df, pd.DataFrame) - assert len(df) == 2 # Two unique entity_ids + assert len(df) == 2 # Two unique entities - # Sort by entity_id for predictable assertions - # Note: entity_id is bytes, so we check features directly conv_rates = sorted(df["conv_rate"].tolist()) assert conv_rates[0] == pytest.approx(0.3) # Driver 2's only value - assert conv_rates[1] == pytest.approx(0.7) # Driver 1's latest value + assert conv_rates[1] == pytest.approx(0.7) # Driver 1's latest + + +# --------------------------------------------------------------------------- +# Tests: get_historical_features (training path — repeated entity IDs) +# --------------------------------------------------------------------------- @_requires_docker -def test_get_historical_features_pit_join( +def test_get_historical_features_training_path( repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView ) -> None: - """Test point-in-time join retrieves correct feature values.""" + """Training path: repeated entity IDs at different timestamps → merge_asof PIT join.""" now = sample_data - # Entity dataframe with driver_id column (must match join keys) entity_df = pd.DataFrame( { "driver_id": [1, 1, 2], "event_timestamp": [ - now - timedelta(hours=1, minutes=30), # Should get conv_rate=0.5 - now - timedelta(minutes=30), # Should get conv_rate=0.6 - now - timedelta(hours=1), # Should get conv_rate=0.3 + now - timedelta(hours=1, minutes=30), # → conv_rate=0.5 (doc 2h ago) + now - timedelta(minutes=30), # → conv_rate=0.6 (doc 1h ago) + now - timedelta(hours=1), # → conv_rate=0.3 (only doc) ], } ) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, feature_views=[driver_fv], feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], @@ -288,28 +292,72 @@ def test_get_historical_features_pit_join( assert isinstance(result_df, pd.DataFrame) assert len(result_df) == 3 - # Sort by driver_id and event_timestamp for predictable assertions result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( drop=True ) - # Driver 1, first request (1.5 hours ago) → should get value from 2 hours ago + # Driver 1, 1.5 hours ago → feature row from 2 hours ago assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) - - # Driver 1, second request (30 min ago) → should get value from 1 hour ago + # Driver 1, 30 min ago → feature row from 1 hour ago assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) - - # Driver 2, request (1 hour ago) → should get value from 2 hours ago + # Driver 2, 1 hour ago → only feature row (2 hours ago) assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) +# --------------------------------------------------------------------------- +# Tests: get_historical_features (scoring path — unique entity IDs) +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_get_historical_features_scoring_path( + repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView +) -> None: + """Scoring path: unique entity IDs → server-side $group dedup, vectorized join.""" + now = sample_data + + # Unique entity IDs → scoring path uses $group + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStore.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + assert len(result_df) == 2 + + # Driver 1: latest value at or before now + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.7) + assert result_df.loc[0, "acc_rate"] == pytest.approx(0.8) + + # Driver 2: only value (2 hours ago) + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.3) + assert result_df.loc[1, "acc_rate"] == pytest.approx(0.95) + + +# --------------------------------------------------------------------------- +# Tests: pull_all_from_table_or_query +# --------------------------------------------------------------------------- + + @_requires_docker def test_pull_all_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceOne + repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSource ) -> None: - """Test pulling all features within a time range (no deduplication).""" + """pull_all returns all rows in the time window without deduplication.""" now = sample_data - job = MongoDBOfflineStoreOne.pull_all_from_table_or_query( + job = MongoDBOfflineStore.pull_all_from_table_or_query( config=repo_config, data_source=driver_source, join_key_columns=["driver_id"], @@ -322,23 +370,27 @@ def test_pull_all_from_table_or_query( df = job.to_df() assert isinstance(df, pd.DataFrame) - # Should get 2 rows: driver 1 (1hr ago, now) - # Excludes: driver 1 from 2 hours ago, driver 2 from 2 hours ago + # 2 rows in window: driver 1 at 1h ago and driver 1 at now. + # Excludes: driver 1 at 2h ago (before start_date) and driver 2 at 2h ago. assert len(df) == 2 +# --------------------------------------------------------------------------- +# Tests: TTL +# --------------------------------------------------------------------------- + + @_requires_docker def test_ttl_excludes_stale_features( repo_config: RepoConfig, mongodb_connection_string: str ) -> None: - """Test that TTL causes stale feature values to be returned as NULL.""" + """TTL causes stale feature values to be returned as NULL.""" client: MongoClient = MongoClient(mongodb_connection_string) db = client["feast_test"] collection = db["feature_history"] now = datetime.now(tz=pytz.UTC) - # Insert docs with different ages ttl_docs = [ { "entity_id": _make_entity_id({"driver_id": 1}), @@ -358,7 +410,7 @@ def test_ttl_excludes_stale_features( collection.insert_many(ttl_docs) client.close() - ttl_source = MongoDBSourceOne( + ttl_source = MongoDBSource( name="driver_stats_ttl", timestamp_field="event_timestamp", ) @@ -376,14 +428,9 @@ def test_ttl_excludes_stale_features( ttl=timedelta(days=1), ) - entity_df = pd.DataFrame( - { - "driver_id": [1, 2], - "event_timestamp": [now, now], - } - ) + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, feature_views=[ttl_fv], feature_refs=["driver_stats_ttl:conv_rate"], @@ -398,102 +445,98 @@ def test_ttl_excludes_stale_features( # Driver 1: fresh → has value assert result_df.loc[0, "conv_rate"] == pytest.approx(0.9) - # Driver 2: stale → NULL + # Driver 2: stale (2 days ago, TTL=1 day) → NULL assert pd.isna(result_df.loc[1, "conv_rate"]) +# --------------------------------------------------------------------------- +# Tests: K-collapse (multiple FVs, same join key signature) +# --------------------------------------------------------------------------- + + @_requires_docker -def test_multiple_feature_views( +def test_k_collapse_multiple_feature_views( repo_config: RepoConfig, mongodb_connection_string: str ) -> None: - """Test joining features from multiple feature views in the same collection.""" + """K=2 FVs sharing the same join key are resolved in a single aggregation group.""" client: MongoClient = MongoClient(mongodb_connection_string) db = client["feast_test"] collection = db["feature_history"] now = datetime.now(tz=pytz.UTC) - # Insert documents for two different feature views - multi_docs = [ - # driver_stats_multi + docs = [ { "entity_id": _make_entity_id({"driver_id": 1}), - "feature_view": "driver_stats_multi", + "feature_view": "driver_stats_k", "features": {"rating": 4.8}, "event_timestamp": now - timedelta(hours=1), "created_at": now - timedelta(hours=1), }, { "entity_id": _make_entity_id({"driver_id": 2}), - "feature_view": "driver_stats_multi", + "feature_view": "driver_stats_k", "features": {"rating": 4.5}, "event_timestamp": now - timedelta(hours=1), "created_at": now - timedelta(hours=1), }, - # vehicle_stats_multi { "entity_id": _make_entity_id({"driver_id": 1}), - "feature_view": "vehicle_stats_multi", + "feature_view": "vehicle_stats_k", "features": {"vehicle_age": 2, "mileage": 50000}, "event_timestamp": now - timedelta(hours=1), "created_at": now - timedelta(hours=1), }, { "entity_id": _make_entity_id({"driver_id": 2}), - "feature_view": "vehicle_stats_multi", + "feature_view": "vehicle_stats_k", "features": {"vehicle_age": 5, "mileage": 120000}, "event_timestamp": now - timedelta(hours=1), "created_at": now - timedelta(hours=1), }, ] - collection.insert_many(multi_docs) + collection.insert_many(docs) client.close() - # Create sources and feature views - driver_source = MongoDBSourceOne(name="driver_stats_multi") - vehicle_source = MongoDBSourceOne(name="vehicle_stats_multi") + driver_source_k = MongoDBSource(name="driver_stats_k") + vehicle_source_k = MongoDBSource(name="vehicle_stats_k") driver_entity = Entity( name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 ) - driver_fv = FeatureView( - name="driver_stats_multi", + driver_fv_k = FeatureView( + name="driver_stats_k", entities=[driver_entity], schema=[ Field(name="driver_id", dtype=Int64), Field(name="rating", dtype=Float64), ], - source=driver_source, + source=driver_source_k, ttl=timedelta(days=1), ) - vehicle_fv = FeatureView( - name="vehicle_stats_multi", + vehicle_fv_k = FeatureView( + name="vehicle_stats_k", entities=[driver_entity], schema=[ Field(name="driver_id", dtype=Int64), Field(name="vehicle_age", dtype=Int64), Field(name="mileage", dtype=Int64), ], - source=vehicle_source, + source=vehicle_source_k, ttl=timedelta(days=1), ) - entity_df = pd.DataFrame( - { - "driver_id": [1, 2], - "event_timestamp": [now, now], - } - ) + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, - feature_views=[driver_fv, vehicle_fv], + feature_views=[driver_fv_k, vehicle_fv_k], feature_refs=[ - "driver_stats_multi:rating", - "vehicle_stats_multi:vehicle_age", - "vehicle_stats_multi:mileage", + "driver_stats_k:rating", + "vehicle_stats_k:vehicle_age", + "vehicle_stats_k:mileage", ], entity_df=entity_df, registry=MagicMock(), @@ -506,37 +549,31 @@ def test_multiple_feature_views( assert len(result_df) == 2 assert set(result_df.columns) >= {"driver_id", "rating", "vehicle_age", "mileage"} - # Driver 1 assert result_df.loc[0, "rating"] == pytest.approx(4.8) assert result_df.loc[0, "vehicle_age"] == 2 assert result_df.loc[0, "mileage"] == 50000 - # Driver 2 assert result_df.loc[1, "rating"] == pytest.approx(4.5) assert result_df.loc[1, "vehicle_age"] == 5 assert result_df.loc[1, "mileage"] == 120000 +# --------------------------------------------------------------------------- +# Tests: overlapping feature names +# --------------------------------------------------------------------------- + + @_requires_docker -def test_multiple_feature_views_overlapping_feature_names( +def test_overlapping_feature_names_full_feature_names( repo_config: RepoConfig, mongodb_connection_string: str ) -> None: - """Regression test: multi-FV join must not raise when FVs share feature names. - - When full_feature_names=False and multiple FVs define the same feature - (e.g. both have a ``score`` column), a naive merge_asof loop would produce - duplicate column names via pandas suffixes (_x, _y) on the second iteration - and then fail with "Passing 'suffixes' which cause duplicate columns" on the - third. The fix pre-renames each FV's columns to a unique per-FV temp prefix - before merging and then renames to the final output name. - """ + """full_feature_names=True prevents collision when multiple FVs share a feature name.""" client: MongoClient = MongoClient(mongodb_connection_string) db = client["feast_test"] collection = db["feature_history"] now = datetime.now(tz=pytz.UTC) - # Three FVs, all sharing feature name ``score``. docs = [] for fv_name, score_val in [ ("fv_overlap_a", 1.0), @@ -562,7 +599,7 @@ def test_multiple_feature_views_overlapping_feature_names( fvs = [] feature_refs = [] for fv_name in ["fv_overlap_a", "fv_overlap_b", "fv_overlap_c"]: - source = MongoDBSourceOne(name=fv_name) + source = MongoDBSource(name=fv_name) fv = FeatureView( name=fv_name, entities=[driver_entity], @@ -578,8 +615,7 @@ def test_multiple_feature_views_overlapping_feature_names( entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - # full_feature_names=True: each FV gets a distinct prefixed column. - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, feature_views=fvs, feature_refs=feature_refs, @@ -595,29 +631,31 @@ def test_multiple_feature_views_overlapping_feature_names( assert "fv_overlap_b__score" in result_df.columns assert "fv_overlap_c__score" in result_df.columns - # Driver 1: scores = base + 1*0.1 assert result_df.loc[0, "fv_overlap_a__score"] == pytest.approx(1.1) assert result_df.loc[0, "fv_overlap_b__score"] == pytest.approx(2.1) assert result_df.loc[0, "fv_overlap_c__score"] == pytest.approx(3.1) - # Driver 2: scores = base + 2*0.1 assert result_df.loc[1, "fv_overlap_a__score"] == pytest.approx(1.2) assert result_df.loc[1, "fv_overlap_b__score"] == pytest.approx(2.2) assert result_df.loc[1, "fv_overlap_c__score"] == pytest.approx(3.2) +# --------------------------------------------------------------------------- +# Tests: compound join keys +# --------------------------------------------------------------------------- + + @_requires_docker def test_compound_join_keys( repo_config: RepoConfig, mongodb_connection_string: str ) -> None: - """Test with compound/composite join keys (multiple entity columns).""" + """Compound join keys (user_id + device_id) are serialized and matched correctly.""" client: MongoClient = MongoClient(mongodb_connection_string) db = client["feast_test"] collection = db["feature_history"] now = datetime.now(tz=pytz.UTC) - # Insert documents with compound keys (user_id + device_id) - compound_docs = [ + docs = [ { "entity_id": _make_entity_id({"user_id": 1, "device_id": "mobile"}), "feature_view": "user_device_features", @@ -628,7 +666,7 @@ def test_compound_join_keys( { "entity_id": _make_entity_id({"user_id": 1, "device_id": "mobile"}), "feature_view": "user_device_features", - "features": {"app_opens": 55}, # Latest for this entity + "features": {"app_opens": 55}, # Latest "event_timestamp": now - timedelta(hours=1), "created_at": now - timedelta(hours=1), }, @@ -647,11 +685,10 @@ def test_compound_join_keys( "created_at": now - timedelta(hours=1), }, ] - collection.insert_many(compound_docs) + collection.insert_many(docs) client.close() - source = MongoDBSourceOne(name="user_device_features") - + source = MongoDBSource(name="user_device_features") user_entity = Entity( name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 ) @@ -671,8 +708,8 @@ def test_compound_join_keys( ttl=timedelta(days=1), ) - # Test pull_latest: should get one row per unique (user_id, device_id) - job = MongoDBOfflineStoreOne.pull_latest_from_table_or_query( + # pull_latest: one row per unique (user_id, device_id) + job = MongoDBOfflineStore.pull_latest_from_table_or_query( config=repo_config, data_source=source, join_key_columns=["user_id", "device_id"], @@ -684,15 +721,14 @@ def test_compound_join_keys( ) df = job.to_df() - assert len(df) == 3 # 3 unique (user_id, device_id) combinations + assert len(df) == 3 - # Verify we got the latest value (55) for user 1, mobile app_opens_values = sorted(df["app_opens"].tolist()) - assert 55 in app_opens_values # Latest for user 1, mobile - assert 10 in app_opens_values # user 1, desktop - assert 25 in app_opens_values # user 2, tablet + assert 10 in app_opens_values + assert 25 in app_opens_values + assert 55 in app_opens_values - # Test get_historical_features with compound keys + # get_historical_features with compound keys entity_df = pd.DataFrame( { "user_id": [1, 1, 2], @@ -701,7 +737,7 @@ def test_compound_join_keys( } ) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, feature_views=[fv], feature_refs=["user_device_features:app_opens"], @@ -714,50 +750,38 @@ def test_compound_join_keys( result_df = job.to_df() assert len(result_df) == 3 - # Sort for predictable assertions result_df = result_df.sort_values(["user_id", "device_id"]).reset_index(drop=True) - # user 1, desktop - assert result_df.loc[0, "app_opens"] == 10 - # user 1, mobile (latest value) - assert result_df.loc[1, "app_opens"] == 55 - # user 2, tablet - assert result_df.loc[2, "app_opens"] == 25 + assert result_df.loc[0, "app_opens"] == 10 # user 1, desktop + assert result_df.loc[1, "app_opens"] == 55 # user 1, mobile (latest) + assert result_df.loc[2, "app_opens"] == 25 # user 2, tablet + + +# --------------------------------------------------------------------------- +# Tests: extra columns in entity_df +# --------------------------------------------------------------------------- @_requires_docker def test_entity_df_with_extra_columns( repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView ) -> None: - """Extra columns in entity_df (e.g. labels) must not corrupt entity key serialization. - - A real training entity_df carries label columns alongside the join key and - timestamp. Before the fix, ``_run_single`` derived entity columns from ALL - non-timestamp DataFrame columns, so a label column like ``trip_success`` - would be included in the serialized entity key. The resulting key would - not match any document in MongoDB and every feature would silently come - back as ``None``. - - This test pins that contract: extra columns must pass through unchanged and - must not affect feature retrieval correctness. - """ + """Label columns in entity_df must not corrupt entity key serialization.""" now = sample_data - # entity_df contains the join key, the PIT timestamp, AND a label column. - # Only ``driver_id`` is a join key; ``trip_success`` is the training label. entity_df = pd.DataFrame( { "driver_id": [1, 1, 2], "event_timestamp": [ - now - timedelta(hours=1, minutes=30), # driver 1 → conv_rate 0.5 - now - timedelta(minutes=30), # driver 1 → conv_rate 0.6 - now - timedelta(hours=1), # driver 2 → conv_rate 0.3 + now - timedelta(hours=1, minutes=30), + now - timedelta(minutes=30), + now - timedelta(hours=1), ], - "trip_success": [1, 0, 1], # label column — must not enter entity key + "trip_success": [1, 0, 1], # label — must not enter entity key } ) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, feature_views=[driver_fv], feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], @@ -770,49 +794,114 @@ def test_entity_df_with_extra_columns( result_df = job.to_df() assert isinstance(result_df, pd.DataFrame) assert len(result_df) == 3 - - # The label column must be preserved unchanged in the result. assert "trip_success" in result_df.columns assert sorted(result_df["trip_success"].tolist()) == [0, 1, 1] - # Features must be non-null — if the label were folded into the entity key - # every lookup would miss and return None here. assert result_df["conv_rate"].notna().all(), ( - "conv_rate is null for all rows — label column was likely included in " - "the entity key serialization, causing every MongoDB lookup to miss." + "conv_rate is null — label column was likely included in entity key serialization." ) result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( drop=True ) - - # Driver 1, 1.5 hours before now → feature row from 2 hours ago assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) - # Driver 1, 30 minutes before now → feature row from 1 hour ago assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) - # Driver 2, 1 hour before now → feature row from 2 hours ago assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) -# ============================================================================= -# persist() tests -# ============================================================================= +# --------------------------------------------------------------------------- +# Tests: INT32 entity key +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_int32_entity_key( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """INT32 entity keys use int32_val in the proto, not int64_val. + + offline_write_batch must serialize INT32 entities with int32_val so that + the bytes match what get_historical_features produces. + """ + driver_entity = Entity( + name="order_id", join_keys=["order_id"], value_type=ValueType.INT32 + ) + source = MongoDBSource(name="order_features") + fv = FeatureView( + name="order_features", + entities=[driver_entity], + schema=[ + Field(name="order_id", dtype=Int32), + Field(name="amount", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + + client: MongoClient = MongoClient(mongodb_connection_string) + client["feast_test"]["feature_history"].drop() + client.close() + + now = datetime.now(tz=pytz.UTC) + + # Write via offline_write_batch (uses mongodb.py's serializer) + df = pd.DataFrame( + { + "order_id": [1, 2], + "amount": [100.0, 200.0], + "event_timestamp": [now - timedelta(hours=1), now - timedelta(hours=1)], + "created_at": [now, now], + } + ) + table = pyarrow.Table.from_pandas(df) + + MongoDBOfflineStore.offline_write_batch( + config=repo_config, + feature_view=fv, + table=table, + progress=None, + ) + + entity_df = pd.DataFrame( + { + "order_id": [1, 2], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStore.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["order_features:amount"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df().sort_values("order_id").reset_index(drop=True) + assert len(result_df) == 2 + assert result_df["amount"].notna().all(), ( + "amount is null — INT32 entity key serialization mismatch between " + "offline_write_batch and get_historical_features." + ) + assert result_df.loc[0, "amount"] == pytest.approx(100.0) + assert result_df.loc[1, "amount"] == pytest.approx(200.0) + + +# --------------------------------------------------------------------------- +# Tests: persist +# --------------------------------------------------------------------------- @_requires_docker -def test_persist_writes_flat_result( +def test_persist_writes_parquet( repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView, - mongodb_connection_string: str, ) -> None: - """persist() writes the flat joined DataFrame to the named destination collection.""" + """persist() writes the joined result to a parquet file.""" now = sample_data - dest = "saved_ds_one_basic" - - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client.close() entity_df = pd.DataFrame( { @@ -821,7 +910,7 @@ def test_persist_writes_flat_result( } ) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, feature_views=[driver_fv], feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], @@ -831,39 +920,36 @@ def test_persist_writes_flat_result( full_feature_names=False, ) - job.persist(SavedDatasetMongoDBStorageOne(database="feast_test", collection=dest)) + with tempfile.NamedTemporaryFile(suffix=".parquet", delete=False) as f: + path = f.name - client = MongoClient(mongodb_connection_string) try: - docs = list(client["feast_test"][dest].find({}, {"_id": 0})) + os.unlink(path) # persist creates the file; it must not already exist + job.persist(SavedDatasetFileStorage(path=path)) + + assert os.path.exists(path) + result_df = pd.read_parquet(path) + assert len(result_df) == 2 + assert {"driver_id", "conv_rate", "acc_rate", "event_timestamp"}.issubset( + result_df.columns + ) finally: - client.close() - - assert len(docs) == 2 - assert {"driver_id", "conv_rate", "acc_rate", "event_timestamp"}.issubset( - docs[0].keys() - ) + if os.path.exists(path): + os.unlink(path) @_requires_docker -def test_persist_raises_if_collection_exists( +def test_persist_raises_if_file_exists( repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView, - mongodb_connection_string: str, ) -> None: - """persist() raises SavedDatasetLocationAlreadyExists when the destination is non-empty.""" + """persist() raises SavedDatasetLocationAlreadyExists when the file already exists.""" now = sample_data - dest = "saved_ds_one_no_overwrite" - - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client["feast_test"][dest].insert_one({"placeholder": True}) - client.close() entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, feature_views=[driver_fv], feature_refs=["driver_stats:conv_rate"], @@ -873,11 +959,15 @@ def test_persist_raises_if_collection_exists( full_feature_names=False, ) - with pytest.raises(SavedDatasetLocationAlreadyExists): - job.persist( - SavedDatasetMongoDBStorageOne(database="feast_test", collection=dest), - allow_overwrite=False, - ) + with tempfile.NamedTemporaryFile(suffix=".parquet", delete=False) as f: + path = f.name + + try: + with pytest.raises(SavedDatasetLocationAlreadyExists): + job.persist(SavedDatasetFileStorage(path=path), allow_overwrite=False) + finally: + if os.path.exists(path): + os.unlink(path) @_requires_docker @@ -885,20 +975,94 @@ def test_persist_allow_overwrite( repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView, - mongodb_connection_string: str, ) -> None: - """persist(allow_overwrite=True) replaces any existing collection contents.""" + """persist(allow_overwrite=True) replaces any existing file.""" now = sample_data - dest = "saved_ds_one_overwrite" + entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) + + job = MongoDBOfflineStore.get_historical_features( + config=repo_config, + feature_views=[driver_fv], + feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + with tempfile.NamedTemporaryFile(suffix=".parquet", delete=False) as f: + path = f.name + f.write(b"stale content") + + try: + job.persist(SavedDatasetFileStorage(path=path), allow_overwrite=True) + + result_df = pd.read_parquet(path) + assert len(result_df) == 1 + assert "driver_id" in result_df.columns + finally: + if os.path.exists(path): + os.unlink(path) + + +# --------------------------------------------------------------------------- +# Tests: offline_write_batch round-trip +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_offline_write_batch_round_trip( + repo_config: RepoConfig, + mongodb_connection_string: str, + driver_fv: FeatureView, +) -> None: + """offline_write_batch writes documents that get_historical_features reads back.""" client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client["feast_test"][dest].insert_many([{"stale": True}, {"stale": True}]) + client["feast_test"]["feature_history"].drop() client.close() - entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) + now = datetime.now(tz=pytz.UTC) - job = MongoDBOfflineStoreOne.get_historical_features( + df = pd.DataFrame( + { + "driver_id": [1, 2], + "conv_rate": [0.8, 0.6], + "acc_rate": [0.9, 0.7], + "event_timestamp": [now - timedelta(hours=1), now - timedelta(hours=1)], + "created_at": [now, now], + } + ) + table = pyarrow.Table.from_pandas(df) + + MongoDBOfflineStore.offline_write_batch( + config=repo_config, + feature_view=driver_fv, + table=table, + progress=None, + ) + + # Verify document structure + client = MongoClient(mongodb_connection_string) + docs = list( + client["feast_test"]["feature_history"].find( + {"feature_view": "driver_stats"}, {"_id": 0} + ) + ) + client.close() + + assert len(docs) == 2 + doc = docs[0] + assert isinstance(doc["entity_id"], bytes), "entity_id must be serialized bytes" + assert doc["feature_view"] == "driver_stats" + assert set(doc["features"].keys()) == {"conv_rate", "acc_rate"} + assert "event_timestamp" in doc + assert "created_at" in doc + + # Round-trip: read back via get_historical_features + entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) + + job = MongoDBOfflineStore.get_historical_features( config=repo_config, feature_views=[driver_fv], feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], @@ -908,185 +1072,302 @@ def test_persist_allow_overwrite( full_feature_names=False, ) - job.persist( - SavedDatasetMongoDBStorageOne(database="feast_test", collection=dest), - allow_overwrite=True, + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + assert len(result_df) == 2 + assert result_df["conv_rate"].notna().all(), ( + "conv_rate is null — offline_write_batch and get_historical_features " + "produced different entity_id bytes." ) + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.8) + assert result_df.loc[0, "acc_rate"] == pytest.approx(0.9) + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) + assert result_df.loc[1, "acc_rate"] == pytest.approx(0.7) - client = MongoClient(mongodb_connection_string) - try: - docs = list(client["feast_test"][dest].find({}, {"_id": 0})) - finally: - client.close() - assert len(docs) == 1 - assert "stale" not in docs[0] - assert "driver_id" in docs[0] +# --------------------------------------------------------------------------- +# Tests: max_ts overshoot (strict_pit semantics) +# --------------------------------------------------------------------------- @_requires_docker -def test_int32_entity_key( - repo_config: RepoConfig, mongodb_connection_string: str +def test_scoring_path_nulls_future_doc( + repo_config: RepoConfig, mongodb_connection_string: str, driver_fv: FeatureView ) -> None: - """INT32 entity keys must round-trip through the correct proto field. + """strict_pit=True: a doc whose timestamp is after the entity request time → NULL. - Python ``int`` is indistinguishable from int32 vs int64 at the Python level. - Without the declared entity type, _serialize_entity_key_from_row always - writes int64_val (8 bytes), but a document written for an INT32 entity uses - int32_val (4 bytes) — different byte sequences, silent NULL return. + Scenario: + Entity A requests features at 09:00. + The only available document is at 10:30 (after the request time). + With strict_pit=True the scoring path must return NULL for entity A. - This test pins that _serialize_entity_key_from_row uses the FeatureView's - declared ValueType to pick int32_val, so the bytes match. + This exercises the ``future_mask`` in the scoring path. The aggregation + uses ``max_ts`` as the server-side upper bound, so the 10:30 doc IS + fetched from MongoDB. The Python-side ``future_mask`` is what nulls it. """ client: MongoClient = MongoClient(mongodb_connection_string) db = client["feast_test"] collection = db["feature_history"] + collection.drop() - now = datetime.now(tz=pytz.UTC) + base = datetime.now(tz=pytz.UTC).replace(microsecond=0) + request_a = base.replace(hour=9, minute=0, second=0) + request_b = base.replace(hour=11, minute=0, second=0) + doc_ts_a = base.replace(hour=10, minute=30, second=0) # after A's request time + doc_ts_b = base.replace(hour=10, minute=0, second=0) # before B's request time + + # Replace with fixed dates to avoid day-boundary issues + from datetime import timezone + + now = datetime.now(tz=timezone.utc) + request_a = now - timedelta(hours=3) # 3 hours ago + request_b = now # now + doc_ts_a = now - timedelta( + hours=1 + ) # 1 hour ago — AFTER request_a, BEFORE request_b + doc_ts_b = now - timedelta(hours=2) # 2 hours ago — before both requests - int32_types = {"order_id": ValueType.INT32} docs = [ { - "entity_id": _make_entity_id({"order_id": 1}, int32_types), - "feature_view": "order_features", - "features": {"amount": 100.0}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_pit", + "features": {"conv_rate": 0.9}, + "event_timestamp": doc_ts_a, + "created_at": doc_ts_a, }, { - "entity_id": _make_entity_id({"order_id": 2}, int32_types), - "feature_view": "order_features", - "features": {"amount": 200.0}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats_pit", + "features": {"conv_rate": 0.5}, + "event_timestamp": doc_ts_b, + "created_at": doc_ts_b, }, ] collection.insert_many(docs) client.close() - source = MongoDBSourceOne(name="order_features") - order_entity = Entity( - name="order_id", join_keys=["order_id"], value_type=ValueType.INT32 + pit_source = MongoDBSource( + name="driver_stats_pit", timestamp_field="event_timestamp" ) - fv = FeatureView( - name="order_features", - entities=[order_entity], + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + pit_fv = FeatureView( + name="driver_stats_pit", + entities=[driver_entity], schema=[ - Field(name="order_id", dtype=Int32), - Field(name="amount", dtype=Float64), + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), ], - source=source, - ttl=timedelta(days=1), + source=pit_source, + ttl=timedelta(days=7), ) + # Unique entity IDs → scoring path entity_df = pd.DataFrame( { - "order_id": [1, 2], - "event_timestamp": [now, now], + "driver_id": [1, 2], + "event_timestamp": [request_a, request_b], } ) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, - feature_views=[fv], - feature_refs=["order_features:amount"], + feature_views=[pit_fv], + feature_refs=["driver_stats_pit:conv_rate"], entity_df=entity_df, registry=MagicMock(), project=repo_config.project, full_feature_names=False, + strict_pit=True, ) - result_df = job.to_df().sort_values("order_id").reset_index(drop=True) + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) assert len(result_df) == 2 - # Without the fix, amount would be None for both rows: int64_val bytes (8 bytes) - # do not match the int32_val bytes (4 bytes) stored in the collection. - assert result_df["amount"].notna().all(), ( - "amount is null — INT32 entity key was serialized as INT64, producing " - "bytes that do not match the stored INT32 documents." + + # Driver 1: only doc is at doc_ts_a which is AFTER request_a → NULL + assert pd.isna(result_df.loc[0, "conv_rate"]), ( + f"Expected NULL for driver 1 (doc at {doc_ts_a} is after request at " + f"{request_a}), got {result_df.loc[0, 'conv_rate']!r}" ) - assert result_df.loc[0, "amount"] == pytest.approx(100.0) - assert result_df.loc[1, "amount"] == pytest.approx(200.0) + + # Driver 2: doc is at doc_ts_b which is BEFORE request_b → value returned + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.5) @_requires_docker -def test_offline_write_batch_round_trip( - repo_config: RepoConfig, - mongodb_connection_string: str, - driver_fv: FeatureView, +def test_scoring_path_nulls_future_doc_chunk_size_1( + repo_config: RepoConfig, mongodb_connection_string: str ) -> None: - """offline_write_batch writes documents that get_historical_features can read back. + """Same as test_scoring_path_nulls_future_doc but with CHUNK_SIZE=1. - This is the end-to-end write→read contract: data written via the standard - Feast write path (offline_write_batch) must be retrievable via the standard - Feast read path (get_historical_features). It also verifies document structure - in the collection. + With CHUNK_SIZE=1 each entity is processed in its own chunk. Per-chunk + ``max_ts`` equals that entity's own request timestamp, so the 10:30 doc + for entity A is never even fetched from MongoDB (server-side filter). + Either way, the result for entity A must be NULL and entity B must have + a value. """ client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"]["feature_history"].drop() + db = client["feast_test"] + collection = db["feature_history"] + collection.drop() + + from datetime import timezone + + now = datetime.now(tz=timezone.utc) + request_a = now - timedelta(hours=3) + request_b = now + doc_ts_a = now - timedelta(hours=1) # after request_a + doc_ts_b = now - timedelta(hours=2) # before both + + docs = [ + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_pit2", + "features": {"conv_rate": 0.9}, + "event_timestamp": doc_ts_a, + "created_at": doc_ts_a, + }, + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats_pit2", + "features": {"conv_rate": 0.5}, + "event_timestamp": doc_ts_b, + "created_at": doc_ts_b, + }, + ] + collection.insert_many(docs) client.close() - now = datetime.now(tz=pytz.UTC) + pit_source = MongoDBSource( + name="driver_stats_pit2", timestamp_field="event_timestamp" + ) + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + pit_fv = FeatureView( + name="driver_stats_pit2", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=pit_source, + ttl=timedelta(days=7), + ) - df = pd.DataFrame( + entity_df = pd.DataFrame( { "driver_id": [1, 2], - "conv_rate": [0.8, 0.6], - "acc_rate": [0.9, 0.7], - "event_timestamp": [now - timedelta(hours=1), now - timedelta(hours=1)], - "created_at": [now, now], + "event_timestamp": [request_a, request_b], } ) - table = pyarrow.Table.from_pandas(df) - - MongoDBOfflineStoreOne.offline_write_batch( - config=repo_config, - feature_view=driver_fv, - table=table, - progress=None, - ) - # Verify document structure in the collection. - client = MongoClient(mongodb_connection_string) - docs = list( - client["feast_test"]["feature_history"].find( - {"feature_view": "driver_stats"}, {"_id": 0} + mongodb_module = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb" + + # Force CHUNK_SIZE=1 so each entity is in its own chunk + with patch(f"{mongodb_module}._CHUNK_SIZE", 1): + job = MongoDBOfflineStore.get_historical_features( + config=repo_config, + feature_views=[pit_fv], + feature_refs=["driver_stats_pit2:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + strict_pit=True, ) + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + + assert len(result_df) == 2 + + # Driver 1: doc at doc_ts_a (after request_a) → NULL regardless of chunk boundary + assert pd.isna(result_df.loc[0, "conv_rate"]), ( + f"Expected NULL for driver 1 with CHUNK_SIZE=1, got " + f"{result_df.loc[0, 'conv_rate']!r}" ) + + # Driver 2: doc at doc_ts_b (before request_b) → value returned + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.5) + + +@_requires_docker +def test_strict_pit_false_returns_future_doc( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """strict_pit=False: future doc IS returned (real-time inference mode). + + Same scenario as test_scoring_path_nulls_future_doc: + Entity A requests at 3 hours ago; only doc is at 1 hour ago (future). + + With strict_pit=False the future_mask is suppressed and the most recent + document is returned regardless of whether it post-dates the request time. + This is correct for real-time scoring where you always want the latest + observation. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + collection.drop() + + from datetime import timezone + + now = datetime.now(tz=timezone.utc) + request_a = now - timedelta(hours=3) + doc_ts_a = now - timedelta(hours=1) # AFTER request_a + + docs = [ + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_pit3", + "features": {"conv_rate": 0.9}, + "event_timestamp": doc_ts_a, + "created_at": doc_ts_a, + }, + ] + collection.insert_many(docs) client.close() - assert len(docs) == 2 - doc = docs[0] - assert isinstance(doc["entity_id"], bytes), "entity_id must be serialized bytes" - assert doc["feature_view"] == "driver_stats" - assert set(doc["features"].keys()) == {"conv_rate", "acc_rate"} - assert "event_timestamp" in doc - assert "created_at" in doc + pit_source = MongoDBSource( + name="driver_stats_pit3", timestamp_field="event_timestamp" + ) + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + pit_fv = FeatureView( + name="driver_stats_pit3", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=pit_source, + ttl=timedelta(days=7), + ) - # Verify round-trip: get_historical_features must retrieve the written data. entity_df = pd.DataFrame( { - "driver_id": [1, 2], - "event_timestamp": [now, now], + "driver_id": [1], + "event_timestamp": [request_a], } ) - job = MongoDBOfflineStoreOne.get_historical_features( + job = MongoDBOfflineStore.get_historical_features( config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], + feature_views=[pit_fv], + feature_refs=["driver_stats_pit3:conv_rate"], entity_df=entity_df, registry=MagicMock(), project=repo_config.project, full_feature_names=False, + strict_pit=False, # Accept future doc ) - result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) - assert len(result_df) == 2 - assert result_df["conv_rate"].notna().all(), ( - "conv_rate is null — offline_write_batch and get_historical_features " - "are producing different entity_id bytes." + result_df = job.to_df().reset_index(drop=True) + assert len(result_df) == 1 + + # With strict_pit=False the doc at doc_ts_a (after request_a) IS returned + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.9), ( + f"Expected conv_rate=0.9 with strict_pit=False but got " + f"{result_df.loc[0, 'conv_rate']!r}. The future_mask may not be " + f"correctly suppressed." ) - assert result_df.loc[0, "conv_rate"] == pytest.approx(0.8) - assert result_df.loc[0, "acc_rate"] == pytest.approx(0.9) - assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) - assert result_df.loc[1, "acc_rate"] == pytest.approx(0.7) diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_native.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_native.py deleted file mode 100644 index 6946dc517b7..00000000000 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_native.py +++ /dev/null @@ -1,1111 +0,0 @@ -""" -Unit tests for the Atlas-first MongoDB native offline store. - -Uses the single-collection schema (all FVs share ``feature_history``, -discriminated by ``feature_view`` field) and performs point-in-time joins -entirely via ``$documents`` + ``$lookup`` aggregation pipelines. - -Requires MongoDB 5.1+ (Atlas M10+, or self-managed). The testcontainer -uses ``mongo:latest`` which satisfies this requirement. - -Docker-dependent tests are marked with ``@_requires_docker`` and are skipped -when Docker is unavailable. -""" - -from datetime import datetime, timedelta -from typing import Dict, Optional -from unittest.mock import MagicMock - -import pandas as pd -import pyarrow -import pytest -import pytz - -pytest.importorskip("pymongo") - -from pymongo import MongoClient -from testcontainers.mongodb import MongoDbContainer - -from feast import Entity, FeatureView, Field -from feast.errors import SavedDatasetLocationAlreadyExists -from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_native import ( - MongoDBOfflineStoreNative, - MongoDBOfflineStoreNativeConfig, - MongoDBSourceNative, - SavedDatasetMongoDBStorageNative, -) -from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto -from feast.protos.feast.types.Value_pb2 import Value as ValueProto -from feast.repo_config import RepoConfig -from feast.types import Float64, Int32, Int64, String -from feast.value_type import ValueType - -# --------------------------------------------------------------------------- -# Docker guard -# --------------------------------------------------------------------------- - -docker_available = False -try: - import docker - - try: - _docker_client = docker.from_env() - _docker_client.ping() - docker_available = True - except Exception: - pass -except ImportError: - pass - -_requires_docker = pytest.mark.skipif( - not docker_available, - reason="Docker is not available or not running.", -) - -ENTITY_KEY_VERSION = 3 - - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - - -def _make_entity_id( - join_keys: dict, - value_types: Optional[Dict[str, ValueType]] = None, -) -> bytes: - """Serialize an entity key from a plain dict, mirroring _serialize_entity_key_from_row. - - Args: - join_keys: Mapping of join key name → value. - value_types: Optional declared ValueType per key. When provided the - correct proto field is used (e.g. INT32 → int32_val). Omit to - get int64_val/string_val inference. - """ - entity_key = EntityKeyProto() - for key in sorted(join_keys.keys()): - entity_key.join_keys.append(key) - val = ValueProto() - value = join_keys[key] - declared_type = value_types.get(key) if value_types else None - if declared_type == ValueType.INT32: - val.int32_val = int(value) - elif declared_type == ValueType.INT64: - val.int64_val = int(value) - elif declared_type == ValueType.STRING: - val.string_val = str(value) - elif declared_type == ValueType.BYTES: - val.bytes_val = bytes(value) - elif declared_type == ValueType.UNIX_TIMESTAMP: - val.unix_timestamp_val = int(value) - else: - if isinstance(value, bool): - val.bool_val = value - elif isinstance(value, int): - val.int64_val = value - elif isinstance(value, str): - val.string_val = value - else: - val.string_val = str(value) - entity_key.entity_values.append(val) - return serialize_entity_key(entity_key, ENTITY_KEY_VERSION) - - -# --------------------------------------------------------------------------- -# Fixtures -# --------------------------------------------------------------------------- - - -@pytest.fixture(scope="module") -def mongodb_container(): - container = MongoDbContainer( - "mongo:latest", - username="test", - password="test", # pragma: allowlist secret - ).with_exposed_ports(27017) - container.start() - yield container - container.stop() - - -@pytest.fixture -def mongodb_connection_string(mongodb_container) -> str: - port = mongodb_container.get_exposed_port(27017) - return f"mongodb://test:test@localhost:{port}" # pragma: allowlist secret - - -@pytest.fixture -def repo_config(mongodb_connection_string: str) -> RepoConfig: - return RepoConfig( - project="test_project", - registry="memory://", - provider="local", - offline_store=MongoDBOfflineStoreNativeConfig( - connection_string=mongodb_connection_string, - database="feast_test", - collection="feature_history", - ), - online_store={"type": "sqlite"}, - entity_key_serialization_version=ENTITY_KEY_VERSION, - ) - - -@pytest.fixture -def sample_data(mongodb_connection_string: str) -> datetime: - """Insert driver_stats documents using the single-collection schema.""" - client: MongoClient = MongoClient(mongodb_connection_string) - db = client["feast_test"] - coll = db["feature_history"] - coll.drop() - - now = datetime.now(tz=pytz.UTC) - coll.insert_many( - [ - { - "entity_id": _make_entity_id({"driver_id": 1}), - "feature_view": "driver_stats", - "features": {"conv_rate": 0.5, "acc_rate": 0.9}, - "event_timestamp": now - timedelta(hours=2), - "created_at": now - timedelta(hours=2), - }, - { - "entity_id": _make_entity_id({"driver_id": 1}), - "feature_view": "driver_stats", - "features": {"conv_rate": 0.6, "acc_rate": 0.85}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"driver_id": 1}), - "feature_view": "driver_stats", - "features": {"conv_rate": 0.7, "acc_rate": 0.8}, - "event_timestamp": now, - "created_at": now, - }, - { - "entity_id": _make_entity_id({"driver_id": 2}), - "feature_view": "driver_stats", - "features": {"conv_rate": 0.3, "acc_rate": 0.95}, - "event_timestamp": now - timedelta(hours=2), - "created_at": now - timedelta(hours=2), - }, - ] - ) - client.close() - return now - - -@pytest.fixture -def driver_source() -> MongoDBSourceNative: - return MongoDBSourceNative( - name="driver_stats", - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - ) - - -@pytest.fixture -def driver_fv(driver_source: MongoDBSourceNative) -> FeatureView: - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - return FeatureView( - name="driver_stats", - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="conv_rate", dtype=Float64), - Field(name="acc_rate", dtype=Float64), - ], - source=driver_source, - ttl=timedelta(days=1), - ) - - -# --------------------------------------------------------------------------- -# Tests — pull_latest_from_table_or_query -# --------------------------------------------------------------------------- - - -@_requires_docker -def test_pull_latest_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceNative -) -> None: - """pull_latest returns one row per entity, taking the most recent document.""" - now = sample_data - job = MongoDBOfflineStoreNative.pull_latest_from_table_or_query( - config=repo_config, - data_source=driver_source, - join_key_columns=["driver_id"], - feature_name_columns=["conv_rate", "acc_rate"], - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - start_date=now - timedelta(days=1), - end_date=now + timedelta(hours=1), - ) - - df = job.to_df() - assert isinstance(df, pd.DataFrame) - assert len(df) == 2 # two unique entity_ids - - conv_rates = sorted(df["conv_rate"].tolist()) - assert conv_rates[0] == pytest.approx(0.3) # driver 2's only value - assert conv_rates[1] == pytest.approx(0.7) # driver 1's latest - - -# --------------------------------------------------------------------------- -# Tests — pull_all_from_table_or_query -# --------------------------------------------------------------------------- - - -@_requires_docker -def test_pull_all_from_table_or_query( - repo_config: RepoConfig, sample_data: datetime, driver_source: MongoDBSourceNative -) -> None: - """pull_all returns every document in the time range without deduplication.""" - now = sample_data - job = MongoDBOfflineStoreNative.pull_all_from_table_or_query( - config=repo_config, - data_source=driver_source, - join_key_columns=["driver_id"], - feature_name_columns=["conv_rate", "acc_rate"], - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - start_date=now - timedelta(hours=1, minutes=30), - end_date=now + timedelta(hours=1), - ) - - df = job.to_df() - assert isinstance(df, pd.DataFrame) - # 2 rows: driver 1 from 1 hr ago and driver 1 from now - # Excludes: driver 1 from 2 hrs ago, driver 2 from 2 hrs ago - assert len(df) == 2 - - -# --------------------------------------------------------------------------- -# Tests — get_historical_features (PIT join) -# --------------------------------------------------------------------------- - - -@_requires_docker -def test_get_historical_features_pit_join( - repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView -) -> None: - """PIT join returns the most recent feature value at or before each entity row's timestamp.""" - now = sample_data - - entity_df = pd.DataFrame( - { - "driver_id": [1, 1, 2], - "event_timestamp": [ - now - timedelta(hours=1, minutes=30), # → conv_rate=0.5 - now - timedelta(minutes=30), # → conv_rate=0.6 - now - timedelta(hours=1), # → conv_rate=0.3 - ], - } - ) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df() - assert isinstance(result_df, pd.DataFrame) - assert len(result_df) == 3 - - result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( - drop=True - ) - - # driver 1, 1.5 hrs ago → feature row from 2 hrs ago - assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) - # driver 1, 30 min ago → feature row from 1 hr ago - assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) - # driver 2, 1 hr ago → feature row from 2 hrs ago - assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) - - -@_requires_docker -def test_ttl_excludes_stale_features( - repo_config: RepoConfig, mongodb_connection_string: str -) -> None: - """Documents older than TTL must come back as NULL, not the stale value.""" - client: MongoClient = MongoClient(mongodb_connection_string) - now = datetime.now(tz=pytz.UTC) - coll = client["feast_test"]["feature_history"] - coll.insert_many( - [ - { - "entity_id": _make_entity_id({"driver_id": 1}), - "feature_view": "driver_stats_ttl", - "features": {"conv_rate": 0.9}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"driver_id": 2}), - "feature_view": "driver_stats_ttl", - "features": {"conv_rate": 0.5}, - "event_timestamp": now - timedelta(days=2), # stale - "created_at": now - timedelta(days=2), - }, - ] - ) - client.close() - - source = MongoDBSourceNative(name="driver_stats_ttl") - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - fv = FeatureView( - name="driver_stats_ttl", - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="conv_rate", dtype=Float64), - ], - source=source, - ttl=timedelta(days=1), - ) - - entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[fv], - feature_refs=["driver_stats_ttl:conv_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) - - # driver 1: fresh → value present - assert result_df.loc[0, "conv_rate"] == pytest.approx(0.9) - # driver 2: stale → NULL - assert pd.isna(result_df.loc[1, "conv_rate"]) - - -@_requires_docker -def test_multiple_feature_views( - repo_config: RepoConfig, mongodb_connection_string: str -) -> None: - """Features from two FVs with the same join key are joined correctly.""" - client: MongoClient = MongoClient(mongodb_connection_string) - now = datetime.now(tz=pytz.UTC) - coll = client["feast_test"]["feature_history"] - coll.insert_many( - [ - { - "entity_id": _make_entity_id({"driver_id": 1}), - "feature_view": "driver_stats_multi", - "features": {"rating": 4.8}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"driver_id": 2}), - "feature_view": "driver_stats_multi", - "features": {"rating": 4.5}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"driver_id": 1}), - "feature_view": "vehicle_stats_multi", - "features": {"vehicle_age": 2, "mileage": 50000}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"driver_id": 2}), - "feature_view": "vehicle_stats_multi", - "features": {"vehicle_age": 5, "mileage": 120000}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - ] - ) - client.close() - - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - driver_fv = FeatureView( - name="driver_stats_multi", - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="rating", dtype=Float64), - ], - source=MongoDBSourceNative(name="driver_stats_multi"), - ttl=timedelta(days=1), - ) - vehicle_fv = FeatureView( - name="vehicle_stats_multi", - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="vehicle_age", dtype=Int64), - Field(name="mileage", dtype=Int64), - ], - source=MongoDBSourceNative(name="vehicle_stats_multi"), - ttl=timedelta(days=1), - ) - - entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[driver_fv, vehicle_fv], - feature_refs=[ - "driver_stats_multi:rating", - "vehicle_stats_multi:vehicle_age", - "vehicle_stats_multi:mileage", - ], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) - assert len(result_df) == 2 - assert {"driver_id", "rating", "vehicle_age", "mileage"}.issubset(result_df.columns) - - assert result_df.loc[0, "rating"] == pytest.approx(4.8) - assert result_df.loc[0, "vehicle_age"] == 2 - assert result_df.loc[0, "mileage"] == 50000 - assert result_df.loc[1, "rating"] == pytest.approx(4.5) - assert result_df.loc[1, "vehicle_age"] == 5 - assert result_df.loc[1, "mileage"] == 120000 - - -@_requires_docker -def test_heterogeneous_join_keys( - repo_config: RepoConfig, mongodb_connection_string: str -) -> None: - """FVs with different join keys must each match only their own documents. - - This is the core correctness invariant of the per-FV entity key design. - driver_stats documents are keyed by serialize({driver_id: X}). - customer_stats documents are keyed by serialize({customer_id: Y}). - The entity_df has both columns. - - A naive implementation would serialize {customer_id: Y, driver_id: X} - (union of all entity columns) and query that single key against both FVs — - producing bytes that match neither stored collection. The correct - implementation serializes each FV's key independently and issues a separate - $lookup per FV. - """ - client: MongoClient = MongoClient(mongodb_connection_string) - now = datetime.now(tz=pytz.UTC) - coll = client["feast_test"]["feature_history"] - coll.insert_many( - [ - # driver_stats: keyed by driver_id only - { - "entity_id": _make_entity_id({"driver_id": 10}), - "feature_view": "driver_stats_het", - "features": {"conv_rate": 0.72}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"driver_id": 20}), - "feature_view": "driver_stats_het", - "features": {"conv_rate": 0.55}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - # customer_stats: keyed by customer_id only - { - "entity_id": _make_entity_id({"customer_id": 100}), - "feature_view": "customer_stats_het", - "features": {"lifetime_spend": 1500.0}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"customer_id": 200}), - "feature_view": "customer_stats_het", - "features": {"lifetime_spend": 800.0}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - ] - ) - client.close() - - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - customer_entity = Entity( - name="customer_id", join_keys=["customer_id"], value_type=ValueType.INT64 - ) - - driver_fv = FeatureView( - name="driver_stats_het", - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="conv_rate", dtype=Float64), - ], - source=MongoDBSourceNative(name="driver_stats_het"), - ttl=timedelta(days=1), - ) - customer_fv = FeatureView( - name="customer_stats_het", - entities=[customer_entity], - schema=[ - Field(name="customer_id", dtype=Int64), - Field(name="lifetime_spend", dtype=Float64), - ], - source=MongoDBSourceNative(name="customer_stats_het"), - ttl=timedelta(days=1), - ) - - # entity_df carries both join keys — exactly the scenario the union-key - # bug would break. - entity_df = pd.DataFrame( - { - "driver_id": [10, 20], - "customer_id": [100, 200], - "event_timestamp": [now, now], - } - ) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[driver_fv, customer_fv], - feature_refs=[ - "driver_stats_het:conv_rate", - "customer_stats_het:lifetime_spend", - ], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) - assert len(result_df) == 2 - - # If the union-key bug were present, both columns would be NULL. - assert result_df["conv_rate"].notna().all(), ( - "conv_rate is null — union join key serialization would include " - "customer_id in the driver_stats entity key, producing bytes that " - "never match stored documents." - ) - assert result_df["lifetime_spend"].notna().all(), ( - "lifetime_spend is null — union join key serialization would include " - "driver_id in the customer_stats entity key." - ) - - assert result_df.loc[0, "conv_rate"] == pytest.approx(0.72) - assert result_df.loc[0, "lifetime_spend"] == pytest.approx(1500.0) - assert result_df.loc[1, "conv_rate"] == pytest.approx(0.55) - assert result_df.loc[1, "lifetime_spend"] == pytest.approx(800.0) - - -@_requires_docker -def test_multiple_feature_views_overlapping_feature_names( - repo_config: RepoConfig, mongodb_connection_string: str -) -> None: - """Three FVs all defining a feature named 'score' must not collide under full_feature_names=True.""" - client: MongoClient = MongoClient(mongodb_connection_string) - now = datetime.now(tz=pytz.UTC) - coll = client["feast_test"]["feature_history"] - - docs = [] - for fv_name, base in [("fv_ol_a", 1.0), ("fv_ol_b", 2.0), ("fv_ol_c", 3.0)]: - for driver_id in [1, 2]: - docs.append( - { - "entity_id": _make_entity_id({"driver_id": driver_id}), - "feature_view": fv_name, - "features": {"score": base + driver_id * 0.1}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - } - ) - coll.insert_many(docs) - client.close() - - driver_entity = Entity( - name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 - ) - fvs = [] - feature_refs = [] - for fv_name in ["fv_ol_a", "fv_ol_b", "fv_ol_c"]: - fv = FeatureView( - name=fv_name, - entities=[driver_entity], - schema=[ - Field(name="driver_id", dtype=Int64), - Field(name="score", dtype=Float64), - ], - source=MongoDBSourceNative(name=fv_name), - ttl=timedelta(days=1), - ) - fvs.append(fv) - feature_refs.append(f"{fv_name}:score") - - entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=fvs, - feature_refs=feature_refs, - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=True, - ) - result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) - - assert len(result_df) == 2 - assert "fv_ol_a__score" in result_df.columns - assert "fv_ol_b__score" in result_df.columns - assert "fv_ol_c__score" in result_df.columns - - assert result_df.loc[0, "fv_ol_a__score"] == pytest.approx(1.1) - assert result_df.loc[0, "fv_ol_b__score"] == pytest.approx(2.1) - assert result_df.loc[0, "fv_ol_c__score"] == pytest.approx(3.1) - assert result_df.loc[1, "fv_ol_a__score"] == pytest.approx(1.2) - assert result_df.loc[1, "fv_ol_b__score"] == pytest.approx(2.2) - assert result_df.loc[1, "fv_ol_c__score"] == pytest.approx(3.2) - - -@_requires_docker -def test_compound_join_keys( - repo_config: RepoConfig, mongodb_connection_string: str -) -> None: - """Compound (multi-column) join keys are serialized and matched correctly.""" - client: MongoClient = MongoClient(mongodb_connection_string) - now = datetime.now(tz=pytz.UTC) - coll = client["feast_test"]["feature_history"] - coll.insert_many( - [ - { - "entity_id": _make_entity_id({"user_id": 1, "device_id": "mobile"}), - "feature_view": "user_device_features", - "features": {"app_opens": 50}, - "event_timestamp": now - timedelta(hours=2), - "created_at": now - timedelta(hours=2), - }, - { - "entity_id": _make_entity_id({"user_id": 1, "device_id": "mobile"}), - "feature_view": "user_device_features", - "features": {"app_opens": 55}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"user_id": 1, "device_id": "desktop"}), - "feature_view": "user_device_features", - "features": {"app_opens": 10}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"user_id": 2, "device_id": "tablet"}), - "feature_view": "user_device_features", - "features": {"app_opens": 25}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - ] - ) - client.close() - - source = MongoDBSourceNative(name="user_device_features") - user_entity = Entity( - name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 - ) - device_entity = Entity( - name="device_id", join_keys=["device_id"], value_type=ValueType.STRING - ) - fv = FeatureView( - name="user_device_features", - entities=[user_entity, device_entity], - schema=[ - Field(name="user_id", dtype=Int64), - Field(name="device_id", dtype=String), - Field(name="app_opens", dtype=Int64), - ], - source=source, - ttl=timedelta(days=1), - ) - - # pull_latest: one row per unique (user_id, device_id) - job = MongoDBOfflineStoreNative.pull_latest_from_table_or_query( - config=repo_config, - data_source=source, - join_key_columns=["user_id", "device_id"], - feature_name_columns=["app_opens"], - timestamp_field="event_timestamp", - created_timestamp_column="created_at", - start_date=now - timedelta(days=1), - end_date=now + timedelta(hours=1), - ) - df = job.to_df() - assert len(df) == 3 - assert set(df["app_opens"].tolist()) == {55, 10, 25} - - # get_historical_features with compound keys - entity_df = pd.DataFrame( - { - "user_id": [1, 1, 2], - "device_id": ["mobile", "desktop", "tablet"], - "event_timestamp": [now, now, now], - } - ) - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[fv], - feature_refs=["user_device_features:app_opens"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - result_df = job.to_df().sort_values(["user_id", "device_id"]).reset_index(drop=True) - assert len(result_df) == 3 - assert result_df.loc[0, "app_opens"] == 10 # user 1, desktop - assert result_df.loc[1, "app_opens"] == 55 # user 1, mobile (latest) - assert result_df.loc[2, "app_opens"] == 25 # user 2, tablet - - -@_requires_docker -def test_entity_df_with_extra_columns( - repo_config: RepoConfig, sample_data: datetime, driver_fv: FeatureView -) -> None: - """Extra (label) columns in entity_df must not corrupt entity key serialization. - - The native store derives join keys from fv.entity_columns, not from all - non-timestamp columns in the entity_df. A label column such as - ``trip_success`` must pass through unchanged and must not enter the key. - """ - now = sample_data - - entity_df = pd.DataFrame( - { - "driver_id": [1, 1, 2], - "event_timestamp": [ - now - timedelta(hours=1, minutes=30), - now - timedelta(minutes=30), - now - timedelta(hours=1), - ], - "trip_success": [1, 0, 1], # label — must not affect entity key - } - ) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df() - assert len(result_df) == 3 - assert "trip_success" in result_df.columns - assert sorted(result_df["trip_success"].tolist()) == [0, 1, 1] - assert result_df["conv_rate"].notna().all(), ( - "conv_rate is null — label column was likely included in the entity key." - ) - - result_df = result_df.sort_values(["driver_id", "event_timestamp"]).reset_index( - drop=True - ) - assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5) - assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) - assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) - - -# --------------------------------------------------------------------------- -# Tests — INT32 entity key round-trip -# --------------------------------------------------------------------------- - - -@_requires_docker -def test_int32_entity_key( - repo_config: RepoConfig, mongodb_connection_string: str -) -> None: - """INT32 entity keys round-trip through the correct proto field (int32_val). - - Without the declared-type fix, Python ``int`` always maps to int64_val - (8 bytes), silently mismatching documents stored with int32_val (4 bytes). - """ - client: MongoClient = MongoClient(mongodb_connection_string) - now = datetime.now(tz=pytz.UTC) - int32_types = {"order_id": ValueType.INT32} - client["feast_test"]["feature_history"].insert_many( - [ - { - "entity_id": _make_entity_id({"order_id": 1}, int32_types), - "feature_view": "order_features", - "features": {"amount": 100.0}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - { - "entity_id": _make_entity_id({"order_id": 2}, int32_types), - "feature_view": "order_features", - "features": {"amount": 200.0}, - "event_timestamp": now - timedelta(hours=1), - "created_at": now - timedelta(hours=1), - }, - ] - ) - client.close() - - order_entity = Entity( - name="order_id", join_keys=["order_id"], value_type=ValueType.INT32 - ) - fv = FeatureView( - name="order_features", - entities=[order_entity], - schema=[ - Field(name="order_id", dtype=Int32), - Field(name="amount", dtype=Float64), - ], - source=MongoDBSourceNative(name="order_features"), - ttl=timedelta(days=1), - ) - - entity_df = pd.DataFrame({"order_id": [1, 2], "event_timestamp": [now, now]}) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[fv], - feature_refs=["order_features:amount"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - result_df = job.to_df().sort_values("order_id").reset_index(drop=True) - assert len(result_df) == 2 - assert result_df["amount"].notna().all(), ( - "amount is null — INT32 entity key was serialized as INT64, producing " - "bytes that do not match the stored INT32 documents." - ) - assert result_df.loc[0, "amount"] == pytest.approx(100.0) - assert result_df.loc[1, "amount"] == pytest.approx(200.0) - - -# --------------------------------------------------------------------------- -# Tests — offline_write_batch round-trip -# --------------------------------------------------------------------------- - - -@_requires_docker -def test_offline_write_batch_round_trip( - repo_config: RepoConfig, - mongodb_connection_string: str, - driver_fv: FeatureView, -) -> None: - """offline_write_batch writes documents that get_historical_features can read back.""" - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"]["feature_history"].drop() - client.close() - - now = datetime.now(tz=pytz.UTC) - df = pd.DataFrame( - { - "driver_id": [1, 2], - "conv_rate": [0.8, 0.6], - "acc_rate": [0.9, 0.7], - "event_timestamp": [now - timedelta(hours=1), now - timedelta(hours=1)], - "created_at": [now, now], - } - ) - table = pyarrow.Table.from_pandas(df) - - MongoDBOfflineStoreNative.offline_write_batch( - config=repo_config, - feature_view=driver_fv, - table=table, - progress=None, - ) - - # Verify document structure - client = MongoClient(mongodb_connection_string) - docs = list( - client["feast_test"]["feature_history"].find( - {"feature_view": "driver_stats"}, {"_id": 0} - ) - ) - client.close() - - assert len(docs) == 2 - doc = docs[0] - assert isinstance(doc["entity_id"], bytes), "entity_id must be serialized bytes" - assert doc["feature_view"] == "driver_stats" - assert set(doc["features"].keys()) == {"conv_rate", "acc_rate"} - assert "event_timestamp" in doc - assert "created_at" in doc - - # Verify round-trip - entity_df = pd.DataFrame({"driver_id": [1, 2], "event_timestamp": [now, now]}) - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) - assert len(result_df) == 2 - assert result_df["conv_rate"].notna().all(), ( - "conv_rate is null — offline_write_batch and get_historical_features " - "are producing different entity_id bytes." - ) - assert result_df.loc[0, "conv_rate"] == pytest.approx(0.8) - assert result_df.loc[0, "acc_rate"] == pytest.approx(0.9) - assert result_df.loc[1, "conv_rate"] == pytest.approx(0.6) - assert result_df.loc[1, "acc_rate"] == pytest.approx(0.7) - - -# --------------------------------------------------------------------------- -# Tests — persist() -# --------------------------------------------------------------------------- - - -@_requires_docker -def test_persist_writes_flat_result( - repo_config: RepoConfig, - sample_data: datetime, - driver_fv: FeatureView, - mongodb_connection_string: str, -) -> None: - """persist() writes the flat joined DataFrame to the named destination collection.""" - now = sample_data - dest = "saved_ds_native_basic" - - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client.close() - - entity_df = pd.DataFrame( - {"driver_id": [1, 2], "event_timestamp": [now, now - timedelta(hours=2)]} - ) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - job.persist( - SavedDatasetMongoDBStorageNative(database="feast_test", collection=dest) - ) - - client = MongoClient(mongodb_connection_string) - try: - docs = list(client["feast_test"][dest].find({}, {"_id": 0})) - finally: - client.close() - - assert len(docs) == 2 - assert {"driver_id", "conv_rate", "acc_rate", "event_timestamp"}.issubset( - docs[0].keys() - ) - - -@_requires_docker -def test_persist_raises_if_collection_exists( - repo_config: RepoConfig, - sample_data: datetime, - driver_fv: FeatureView, - mongodb_connection_string: str, -) -> None: - """persist() raises SavedDatasetLocationAlreadyExists when destination is non-empty.""" - now = sample_data - dest = "saved_ds_native_no_overwrite" - - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client["feast_test"][dest].insert_one({"placeholder": True}) - client.close() - - entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - - with pytest.raises(SavedDatasetLocationAlreadyExists): - job.persist( - SavedDatasetMongoDBStorageNative(database="feast_test", collection=dest), - allow_overwrite=False, - ) - - -@_requires_docker -def test_persist_allow_overwrite( - repo_config: RepoConfig, - sample_data: datetime, - driver_fv: FeatureView, - mongodb_connection_string: str, -) -> None: - """persist(allow_overwrite=True) replaces any existing collection contents.""" - now = sample_data - dest = "saved_ds_native_overwrite" - - client: MongoClient = MongoClient(mongodb_connection_string) - client["feast_test"][dest].drop() - client["feast_test"][dest].insert_many([{"stale": True}, {"stale": True}]) - client.close() - - entity_df = pd.DataFrame({"driver_id": [1], "event_timestamp": [now]}) - - job = MongoDBOfflineStoreNative.get_historical_features( - config=repo_config, - feature_views=[driver_fv], - feature_refs=["driver_stats:conv_rate", "driver_stats:acc_rate"], - entity_df=entity_df, - registry=MagicMock(), - project=repo_config.project, - full_feature_names=False, - ) - job.persist( - SavedDatasetMongoDBStorageNative(database="feast_test", collection=dest), - allow_overwrite=True, - ) - - client = MongoClient(mongodb_connection_string) - try: - docs = list(client["feast_test"][dest].find({}, {"_id": 0})) - finally: - client.close() - - assert len(docs) == 1 - assert "stale" not in docs[0] - assert "driver_id" in docs[0] diff --git a/sdk/python/tests/universal/feature_repos/repo_configuration.py b/sdk/python/tests/universal/feature_repos/repo_configuration.py index 2033d416032..252c9a48925 100644 --- a/sdk/python/tests/universal/feature_repos/repo_configuration.py +++ b/sdk/python/tests/universal/feature_repos/repo_configuration.py @@ -108,30 +108,22 @@ ] ) -# MongoDB offline stores (require testcontainers and pymongo) +# MongoDB offline store (requires testcontainers and pymongo) if os.getenv("FEAST_LOCAL_ONLINE_CONTAINER", "False") == "True": try: from tests.universal.feature_repos.universal.data_sources.mongodb import ( - MongoDBManyDataSourceCreator, - # MongoDBOneDataSourceCreator, # TODO: Not registered - see TODO in mongodb.py + MongoDBDataSourceCreator, ) AVAILABLE_OFFLINE_STORES.extend( [ - ("local", MongoDBManyDataSourceCreator), - # TODO: MongoDBOneDataSourceCreator requires DataSourceCreator interface - # changes to pass entity/join key info. See mongodb.py for details. - # ("local", MongoDBOneDataSourceCreator), + ("local", MongoDBDataSourceCreator), ] ) - OFFLINE_STORE_TO_PROVIDER_CONFIG["mongodb_many"] = ( + OFFLINE_STORE_TO_PROVIDER_CONFIG["mongodb"] = ( "local", - MongoDBManyDataSourceCreator, + MongoDBDataSourceCreator, ) - # OFFLINE_STORE_TO_PROVIDER_CONFIG["mongodb_one"] = ( - # "local", - # MongoDBOneDataSourceCreator, - # ) except ImportError: pass # pymongo or testcontainers not installed diff --git a/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py b/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py index 0a0cfe648cc..422cae278d2 100644 --- a/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py +++ b/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py @@ -1,11 +1,9 @@ """ -MongoDB DataSourceCreator implementations for universal Feast tests. - -Provides two implementations matching the two offline store schemas: -- MongoDBManyDataSourceCreator: One collection per FeatureView (Many) -- MongoDBOneDataSourceCreator: Single shared collection (One) +MongoDB DataSourceCreator implementation for universal Feast tests. """ +import os +import tempfile from typing import Any, Dict, Optional import pandas as pd @@ -15,145 +13,26 @@ from feast.data_source import DataSource from feast.feature_logging import LoggingDestination from feast.infra.key_encoding_utils import serialize_entity_key -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_many import ( - MongoDBOfflineStoreManyConfig, - MongoDBSourceMany, - SavedDatasetMongoDBStorageMany, -) -from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb_one import ( - MongoDBOfflineStoreOneConfig, - MongoDBSourceOne, +from feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb import ( + MongoDBOfflineStoreConfig, + MongoDBSource, ) +from feast.infra.offline_stores.file_source import SavedDatasetFileStorage from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto from feast.repo_config import FeastConfigBaseModel -from feast.saved_dataset import SavedDatasetStorage from tests.universal.feature_repos.universal.data_source_creator import ( DataSourceCreator, ) -# Import pymongo - will be available since we're testing MongoDB try: from pymongo import MongoClient except ImportError: MongoClient = None # type: ignore -class MongoDBManyDataSourceCreator(DataSourceCreator): - """DataSourceCreator for MongoDBOfflineStoreMany (one collection per FeatureView).""" - - def __init__(self, project_name: str, *args, **kwargs): - super().__init__(project_name) - self.container = MongoDbContainer( - "mongo:7.0", - username="test", - password="test", # pragma: allowlist secret - ).with_exposed_ports(27017) - self.container.start() - self.port = self.container.get_exposed_port(27017) - self.connection_string = ( - f"mongodb://test:test@localhost:{self.port}" # pragma: allowlist secret - ) - self.database = f"feast_test_{project_name}" - self.collections_created: list[str] = [] - - def create_data_source( - self, - df: pd.DataFrame, - destination_name: str, - created_timestamp_column: str = "created_ts", - field_mapping: Optional[Dict[str, str]] = None, - timestamp_field: Optional[str] = "ts", - ) -> DataSource: - """Create a MongoDB data source by inserting df into a collection.""" - collection_name = self.get_prefixed_table_name(destination_name) - - # Insert data into MongoDB - client: Any = MongoClient(self.connection_string, tz_aware=True) - try: - coll = client[self.database][collection_name] - coll.drop() # Clean slate - records = df.to_dict("records") - if records: - coll.insert_many(records) - self.collections_created.append(collection_name) - finally: - client.close() - - return MongoDBSourceMany( - name=destination_name, - database=self.database, - collection=collection_name, - timestamp_field=timestamp_field or "ts", - created_timestamp_column=created_timestamp_column, - field_mapping=field_mapping, - ) - - def get_prefixed_table_name(self, suffix: str) -> str: - return f"{self.project_name}_{suffix}" - - def create_offline_store_config(self) -> FeastConfigBaseModel: - return MongoDBOfflineStoreManyConfig( - connection_string=self.connection_string, - database=self.database, - ) - - def create_saved_dataset_destination(self) -> SavedDatasetStorage: - return SavedDatasetMongoDBStorageMany( - database=self.database, - collection=f"{self.project_name}_saved_dataset", - ) - - def create_logged_features_destination(self) -> LoggingDestination: - # MongoDB doesn't have a native LoggingDestination yet - # Return None or raise NotImplementedError for now - raise NotImplementedError( - "MongoDB LoggingDestination not implemented. " - "Tests requiring logging features will be skipped." - ) - - def teardown(self): - """Clean up: drop collections and stop container.""" - try: - client: Any = MongoClient(self.connection_string, tz_aware=True) - try: - db = client[self.database] - for coll_name in self.collections_created: - db[coll_name].drop() - finally: - client.close() - except Exception: - pass # Container may already be stopped - self.container.stop() - - @staticmethod - def test_markers() -> list: - """Mark tests as requiring MongoDB.""" - return [pytest.mark.mongodb] - - -class MongoDBOneDataSourceCreator(DataSourceCreator): - """DataSourceCreator for MongoDBOfflineStoreOne (single shared collection). - - This implementation uses the nested features schema where all FeatureViews - share a single collection with a discriminator field. - - TODO: This DataSourceCreator has a fundamental limitation. The One schema - requires knowing which columns are join keys vs features to properly - serialize entity_id and nest features. However, create_data_source() only - receives a DataFrame and column names - it doesn't have access to Entity - definitions that specify join keys. - - Current workaround uses heuristics (columns ending in '_id' with int/string - dtype), which is fragile. A proper fix would require modifying the - DataSourceCreator interface to pass entity/join key information to - create_data_source(), which is a Feast core change. - - For now, universal tests may fail for FeatureViews where the heuristic - doesn't correctly identify join keys. Use unit tests in - tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_one.py - for comprehensive testing of the One implementation. - """ +class MongoDBDataSourceCreator(DataSourceCreator): + """DataSourceCreator for the MongoDB offline store (single shared collection).""" ENTITY_KEY_VERSION = 3 @@ -171,9 +50,7 @@ def __init__(self, project_name: str, *args, **kwargs): ) self.database = f"feast_test_{project_name}" self.collection = "feature_history" - self.feature_views_created: list[str] = [] - # Track entity key columns per feature view for serialization - self._entity_key_columns: Dict[str, list[str]] = {} + self._entity_key_columns: Dict[str, list] = {} def _serialize_entity_key(self, row: pd.Series, join_keys: list[str]) -> bytes: """Serialize entity key columns to bytes. @@ -188,7 +65,7 @@ def _serialize_entity_key(self, row: pd.Series, join_keys: list[str]) -> bytes: entity_key.join_keys.append(key) val = ValueProto() value = row[key] - # bool must be checked before int: bool is a subclass of int in Python + # bool must be checked before int: bool is a subclass of int if isinstance(value, bool): val.bool_val = value elif isinstance(value, int): @@ -224,21 +101,17 @@ def create_data_source( timestamp_cols = {timestamp_field, created_timestamp_column} all_cols = set(df.columns) - timestamp_cols - {None} - # Heuristic: identify join keys - # 1. Must end with "_id" or be a known key name - # 2. Must be integer or string type (not float) + # Heuristic: identify join keys (columns ending with _id or known key names) join_keys = [] for c in all_cols: if c.endswith("_id") or c in {"driver", "customer", "entity"}: dtype = df[c].dtype - # Only integer or string types can be join keys if dtype in ("int64", "int32", "object") or str(dtype).startswith( "int" ): join_keys.append(c) if not join_keys: - # Fallback: first integer column for c in all_cols: if df[c].dtype in ("int64", "int32") or str(df[c].dtype).startswith( "int" @@ -247,17 +120,13 @@ def create_data_source( break feature_cols = [c for c in all_cols if c not in join_keys] - - # Store for later use self._entity_key_columns[destination_name] = join_keys - # Transform to One schema docs = [] for _, row in df.iterrows(): entity_id = self._serialize_entity_key(row, join_keys) features = {col: row[col] for col in feature_cols if pd.notna(row.get(col))} - - doc = { + doc: Dict[str, Any] = { "entity_id": entity_id, "feature_view": destination_name, "features": features, @@ -266,7 +135,6 @@ def create_data_source( doc["event_timestamp"] = row[timestamp_field] if created_timestamp_column and created_timestamp_column in row: doc["created_at"] = row[created_timestamp_column] - docs.append(doc) # Insert into MongoDB @@ -275,11 +143,10 @@ def create_data_source( coll = client[self.database][self.collection] if docs: coll.insert_many(docs) - self.feature_views_created.append(destination_name) finally: client.close() - return MongoDBSourceOne( + return MongoDBSource( name=destination_name, timestamp_field="event_timestamp", created_timestamp_column="created_at" if created_timestamp_column else None, @@ -290,23 +157,25 @@ def get_prefixed_table_name(self, suffix: str) -> str: return f"{self.project_name}_{suffix}" def create_offline_store_config(self) -> FeastConfigBaseModel: - return MongoDBOfflineStoreOneConfig( + return MongoDBOfflineStoreConfig( connection_string=self.connection_string, database=self.database, collection=self.collection, ) - def create_saved_dataset_destination(self) -> SavedDatasetStorage: - # One implementation doesn't have SavedDatasetStorage yet - raise NotImplementedError( - "MongoDBOfflineStoreOne SavedDatasetStorage not implemented." + def create_saved_dataset_destination(self) -> SavedDatasetFileStorage: + # Saved datasets are written as parquet via SavedDatasetFileStorage. + # A fresh temp file is created per call; teardown() does not clean it up + # because the test framework owns the lifetime of saved datasets. + path = os.path.join( + tempfile.mkdtemp(), f"{self.project_name}_saved_dataset.parquet" ) + return SavedDatasetFileStorage(path=path) def create_logged_features_destination(self) -> LoggingDestination: raise NotImplementedError("MongoDB LoggingDestination not implemented.") def teardown(self): - """Clean up: drop the collection and stop container.""" try: client: Any = MongoClient(self.connection_string, tz_aware=True) try: @@ -319,5 +188,4 @@ def teardown(self): @staticmethod def test_markers() -> list: - """Mark tests as requiring MongoDB.""" return [pytest.mark.mongodb] From a6dca865dda5595c1acfa8e86191903549a36131 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 23 Apr 2026 16:08:27 -0400 Subject: [PATCH 56/76] Fixes strict_pit_false unit test. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 58ef15182b1..a8f3e5ffc4e 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -591,9 +591,14 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: for n in group_fv_names if fv_by_name.get(n) and (ttl := fv_by_name[n].ttl) is not None ] - ts_filter: Dict[str, Any] = {"$lte": max_ts} + # strict_pit=True: server upper bound is the chunk's max request time, + # preventing the server from returning future documents. + # strict_pit=False: no upper bound — the $group $first (sort DESC) will + # return the most recent doc regardless of when it was written. + ts_filter: Dict[str, Any] = {"$lte": max_ts} if strict_pit else {} if ttls: - ts_filter["$gte"] = max_ts - min(ttls) + ref_ts = max_ts if strict_pit else datetime.now(tz=timezone.utc) + ts_filter["$gte"] = ref_ts - min(ttls) all_docs: List[Dict] = [] for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): From 055d23874a888b01f77328f4e7988c82df7abc65 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 16:11:18 -0400 Subject: [PATCH 57/76] Fix MongoDB offline store: projection keying, TTL bounds, field mapping, and schema inference - Key get_historical_features internals by projection name (fv.projection.name_to_use()) instead of fv.name, fixing entity mapping for multi-projection feature views - Use min_ts - fv.ttl for TTL lower bound instead of max_ts - fv.ttl, so documents needed for early entity rows in a chunk are not excluded - Replace pd.json_normalize with manual dict extraction to preserve complex types (Map/Struct/Array) and apply reverse field_mapping for source column lookup - Add get_table_column_names_and_types to MongoDBSource for feast apply support - Use batch_source.feature_view_name in offline_write_batch so pushed data lands in the same collection partition as initial ingest Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 455 ++++++++++-------- 1 file changed, 262 insertions(+), 193 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index a8f3e5ffc4e..b5149a4002c 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -39,7 +39,7 @@ import warnings from collections import defaultdict -from datetime import datetime, timedelta, timezone +from datetime import datetime, timezone from typing import ( Any, Callable, @@ -194,6 +194,48 @@ def _to_proto_impl(self) -> DataSourceProto: def get_table_query_string(self) -> str: return self.name + def get_table_column_names_and_types( + self, config: RepoConfig + ) -> List[Tuple[str, str]]: + """Infer column names and types by reading a sample document from MongoDB.""" + if MongoClient is None: + raise FeastExtrasDependencyImportError("pymongo", "mongodb") + client: Any = MongoClient(config.offline_store.connection_string) + try: + coll = client[config.offline_store.database][ + config.offline_store.collection + ] + doc = coll.find_one({"feature_view": self.feature_view_name}) + if doc is None: + return [] + result: List[Tuple[str, str]] = [] + # Entity key is binary — join keys are inferred from the FeatureView, + # not from the document. Expose event_timestamp and created_at. + if "event_timestamp" in doc: + result.append(("event_timestamp", "datetime")) + if "created_at" in doc: + result.append(("created_at", "datetime")) + features = doc.get("features", {}) + if isinstance(features, dict): + for k, v in features.items(): + if isinstance(v, int): + result.append((k, "int64")) + elif isinstance(v, float): + result.append((k, "float64")) + elif isinstance(v, str): + result.append((k, "string")) + elif isinstance(v, bool): + result.append((k, "bool")) + elif isinstance(v, list): + result.append((k, "list")) + elif isinstance(v, dict): + result.append((k, "dict")) + else: + result.append((k, "object")) + return result + finally: + client.close() + @staticmethod def source_datatype_to_feast_value_type() -> Callable[[str], ValueType]: return mongodb_to_feast_value_type @@ -513,26 +555,61 @@ def get_historical_features( entity_schema = dict(zip(entity_df.columns, entity_df.dtypes)) event_timestamp_col = infer_event_timestamp_from_entity_df(entity_schema) + # Feature refs use projection names (e.g. "origin:temperature") fv_to_features: Dict[str, List[str]] = defaultdict(list) for ref in feature_refs: fv_name, feat_name = ref.split(":", 1) fv_to_features[fv_name].append(feat_name) - fv_by_name = {fv.name: fv for fv in feature_views} - fv_join_keys_by_name: Dict[str, List[str]] = { - fv.name: list(get_expected_join_keys(project, [fv], registry)) + # All dicts keyed by projection name (name_to_use), not fv.name, + # because entity mapping creates multiple projections of the same FV. + fv_by_proj: Dict[str, FeatureView] = { + fv.projection.name_to_use(): fv for fv in feature_views + } + + # projection_name → MongoDB feature_view discriminator value + # (the data source name, NOT the FeatureView name) + fv_mongo_name: Dict[str, str] = {} + for fv in feature_views: + proj = fv.projection.name_to_use() + src = fv.batch_source + fv_mongo_name[proj] = ( + src.feature_view_name + if isinstance(src, MongoDBSource) + else getattr(src, "name", fv.name) + ) + + # projection_name → mapped join keys (as in entity_df columns) + fv_mapped_join_keys: Dict[str, List[str]] = { + fv.projection.name_to_use(): list( + get_expected_join_keys(project, [fv], registry) + ) for fv in feature_views } - fv_join_key_types_by_name: Dict[str, Dict[str, ValueType]] = { - fv.name: { - fv.projection.join_key_map.get( - ec.name, ec.name - ): ec.dtype.to_value_type() - for ec in fv.entity_columns + + # projection_name → {mapped_key → original_key} + fv_reverse_jk: Dict[str, Dict[str, str]] = { + fv.projection.name_to_use(): { + v: k for k, v in fv.projection.join_key_map.items() } for fv in feature_views } + # projection_name → original join key types (keyed by original name) + fv_jk_types_original: Dict[str, Dict[str, ValueType]] = { + fv.projection.name_to_use(): { + ec.name: ec.dtype.to_value_type() for ec in fv.entity_columns + } + for fv in feature_views + } + + # projection_name → reverse field_mapping (feast_name → source_col_name) + fv_reverse_fm: Dict[str, Dict[str, str]] = {} + for fv in feature_views: + proj = fv.projection.name_to_use() + fm = fv.batch_source.field_mapping if fv.batch_source else {} + fv_reverse_fm[proj] = {v: k for k, v in fm.items()} if fm else {} + CHUNK_SIZE = _CHUNK_SIZE MONGO_BATCH_SIZE = _MONGO_BATCH_SIZE @@ -554,92 +631,110 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: ) max_ts = result[event_timestamp_col].max() + min_ts = result[event_timestamp_col].min() # Detect scoring vs training path once per chunk. - # Scoring: unique entity IDs across ALL join key combinations. - # Training: repeated entity IDs → must use merge_asof. all_entity_id_cols = list( - {jk for jks in fv_join_keys_by_name.values() for jk in jks} + {jk for jks in fv_mapped_join_keys.values() for jk in jks} & set(result.columns) ) scoring_path = result[all_entity_id_cols].drop_duplicates().shape[0] == len( result ) - # Group FVs by join key signature to collapse K → |unique key sets| - fv_groups: Dict[Tuple[str, ...], List[str]] = defaultdict(list) - for fv_name in fv_to_features: - sig = tuple(sorted(fv_join_keys_by_name[fv_name])) - fv_groups[sig].append(fv_name) - - for join_key_sig, group_fv_names in fv_groups.items(): - join_keys = list(join_key_sig) - # Use the type map from the first FV in the group (keys are shared) - key_types = fv_join_key_types_by_name[group_fv_names[0]] + # Process each feature view projection independently. + # (Different projections of the same FV have different + # entity key mappings and must be handled separately.) + for proj_name, features in fv_to_features.items(): + fv = fv_by_proj.get(proj_name) + if fv is None: + for feat in features: + col = f"{proj_name}__{feat}" if full_feature_names else feat + result[col] = None + continue - result["_fv_entity_id"] = result.apply( - lambda row: _serialize_entity_key_from_row( - row, join_keys, entity_key_version, key_types - ), - axis=1, - ) - unique_entity_ids = result["_fv_entity_id"].unique().tolist() - - # Use the most conservative TTL across the group for the lower bound - ttls: List[timedelta] = [ - ttl - for n in group_fv_names - if fv_by_name.get(n) and (ttl := fv_by_name[n].ttl) is not None - ] - # strict_pit=True: server upper bound is the chunk's max request time, - # preventing the server from returning future documents. - # strict_pit=False: no upper bound — the $group $first (sort DESC) will - # return the most recent doc regardless of when it was written. + mongo_fv_name = fv_mongo_name[proj_name] + mapped_keys = fv_mapped_join_keys[proj_name] + reverse_jk = fv_reverse_jk[proj_name] + orig_key_types = fv_jk_types_original[proj_name] + reverse_fm = fv_reverse_fm[proj_name] + + # Serialize entity keys: read values from MAPPED columns in + # entity_df, but serialize with ORIGINAL join key names to + # match the bytes stored in MongoDB. + _mk = mapped_keys + _rjk = reverse_jk + _okt = orig_key_types + + def _ser(row, __mk=_mk, __rjk=_rjk, __okt=_okt): + ek = EntityKeyProto() + orig_keys = sorted([__rjk.get(m, m) for m in __mk]) + o2m = {__rjk.get(m, m): m for m in __mk} + for ok in orig_keys: + mk = o2m[ok] + val = row[mk] + ek.join_keys.append(ok) + pv = ValueProto() + vt = __okt.get(ok, ValueType.UNKNOWN) + if vt == ValueType.INT32: + pv.int32_val = int(val) + elif vt == ValueType.INT64 or isinstance(val, int): + pv.int64_val = int(val) + elif vt == ValueType.STRING or isinstance(val, str): + pv.string_val = str(val) + elif isinstance(val, float): + pv.double_val = float(val) + else: + pv.int64_val = int(val) + ek.entity_values.append(pv) + return serialize_entity_key(ek, entity_key_version) + + eid_col = f"_eid_{proj_name}" + result[eid_col] = result.apply(_ser, axis=1) + unique_eids = result[eid_col].unique().tolist() + + # TTL filter — use min_ts for the lower bound so that + # documents needed for early entity rows are included. + # Per-row TTL enforcement happens in the merge_asof path. + fv_ttl = fv.ttl if fv else None ts_filter: Dict[str, Any] = {"$lte": max_ts} if strict_pit else {} - if ttls: - ref_ts = max_ts if strict_pit else datetime.now(tz=timezone.utc) - ts_filter["$gte"] = ref_ts - min(ttls) + if fv_ttl: + lower_ref = min_ts if strict_pit else datetime.now(tz=timezone.utc) + ts_filter["$gte"] = lower_ref - fv_ttl + # Query MongoDB all_docs: List[Dict] = [] - for i in range(0, len(unique_entity_ids), MONGO_BATCH_SIZE): - batch_ids = unique_entity_ids[i : i + MONGO_BATCH_SIZE] + for i in range(0, len(unique_eids), MONGO_BATCH_SIZE): + batch_ids = unique_eids[i : i + MONGO_BATCH_SIZE] + match_q: Dict[str, Any] = { + "entity_id": {"$in": batch_ids}, + "feature_view": mongo_fv_name, + } + if ts_filter: + match_q["event_timestamp"] = ts_filter if scoring_path: - # Server-side dedup: one doc per (entity_id, feature_view). - # The compound index backs $match→$sort→$group entirely. pipeline: List[Dict] = [ - { - "$match": { - "entity_id": {"$in": batch_ids}, - "feature_view": {"$in": group_fv_names}, - "event_timestamp": ts_filter, - } - }, + {"$match": match_q}, { "$sort": { "entity_id": 1, - "feature_view": 1, "event_timestamp": -1, "created_at": -1, } }, { "$group": { - "_id": { - "eid": "$entity_id", - "fv": "$feature_view", - }, + "_id": "$entity_id", "event_timestamp": {"$first": "$event_timestamp"}, "features": {"$first": "$features"}, "created_at": {"$first": "$created_at"}, } }, - # Reshape to flat document matching the training-path schema { "$project": { "_id": 0, - "entity_id": "$_id.eid", - "feature_view": "$_id.fv", + "entity_id": "$_id", "event_timestamp": 1, "features": 1, "created_at": 1, @@ -647,151 +742,120 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: }, ] else: - # Training path: fetch all docs in window; merge_asof - # handles per-row PIT precision. - pipeline = [ - { - "$match": { - "entity_id": {"$in": batch_ids}, - "feature_view": {"$in": group_fv_names}, - "event_timestamp": ts_filter, - } - }, - ] + pipeline = [{"$match": match_q}] all_docs.extend(list(coll.aggregate(pipeline))) - # Split returned docs by feature_view, then process per FV - docs_by_fv: Dict[str, List[Dict]] = defaultdict(list) - for doc in all_docs: - docs_by_fv[doc["feature_view"]].append(doc) - - for fv_name in group_fv_names: - features = fv_to_features[fv_name] - fv = fv_by_name.get(fv_name) - fv_docs = docs_by_fv.get(fv_name, []) - - if not fv_docs: - for feat in features: - col = f"{fv_name}__{feat}" if full_feature_names else feat - result[col] = None - continue - - fv_df = pd.DataFrame(fv_docs) - fv_df = fv_df.rename(columns={"entity_id": "_fv_entity_id"}) + if not all_docs: + for feat in features: + col = f"{proj_name}__{feat}" if full_feature_names else feat + result[col] = None + result = result.drop(columns=[eid_col], errors="ignore") + continue - if "features" in fv_df.columns: - # Expand features dict in one vectorized pass - feat_expanded = pd.json_normalize(fv_df["features"].tolist()) - for feat in features: - fv_df[feat] = ( - feat_expanded[feat].values - if feat in feat_expanded.columns - else None + fv_df = pd.DataFrame(all_docs) + fv_df = fv_df.rename(columns={"entity_id": eid_col}) + + # Extract features from nested dict, applying reverse field_mapping. + # Using .apply() instead of json_normalize preserves complex types + # (dicts for Map/Struct, lists for Array). + if "features" in fv_df.columns: + for feat in features: + src_col = reverse_fm.get(feat, feat) + fv_df[feat] = fv_df["features"].apply( + lambda d, _s=src_col: ( + d.get(_s) if isinstance(d, dict) else None ) - fv_df = fv_df.drop(columns=["features"]) - - if fv_df["event_timestamp"].dt.tz is None: - fv_df["event_timestamp"] = pd.to_datetime( - fv_df["event_timestamp"], utc=True ) + fv_df = fv_df.drop(columns=["features"]) - if scoring_path: - # Vectorized join: merge fv_df onto result by entity_id, - # then null out rows where the server returned a doc that - # is too recent (max_ts approximation) or TTL-stale. - fv_join_cols = ["_fv_entity_id", "event_timestamp"] + [ - f for f in features if f in fv_df.columns - ] - fv_join = fv_df[fv_join_cols].rename( - columns={"event_timestamp": "_fv_ts"} - ) - # left merge: entities with no match get NaN features - merged = result[["_fv_entity_id", event_timestamp_col]].merge( - fv_join, on="_fv_entity_id", how="left" - ) - - # Mask: fv doc is outside valid window for entity request. - # strict_pit=True (default): null out docs from the future - # relative to the entity request timestamp (max_ts overshoot). - # strict_pit=False: accept the most recent doc regardless of - # whether it post-dates the request (real-time inference). - if strict_pit: - future_mask = merged["_fv_ts"] > merged[event_timestamp_col] - else: - future_mask = pd.Series( - [False] * len(merged), index=merged.index - ) - if fv and fv.ttl: - ttl_mask = merged["_fv_ts"] < ( - merged[event_timestamp_col] - fv.ttl - ) - bad_mask = future_mask | ttl_mask - else: - bad_mask = future_mask + if fv_df["event_timestamp"].dt.tz is None: + fv_df["event_timestamp"] = pd.to_datetime( + fv_df["event_timestamp"], utc=True + ) - for feat in features: - col = f"{fv_name}__{feat}" if full_feature_names else feat - vals = ( - merged[feat].copy() - if feat in merged.columns - else pd.Series([None] * len(merged), dtype=object) - ) - vals[bad_mask | merged["_fv_ts"].isna()] = None - result[col] = vals.values + if scoring_path: + fv_join_cols = [eid_col, "event_timestamp"] + [ + f for f in features if f in fv_df.columns + ] + fv_join = fv_df[fv_join_cols].rename( + columns={"event_timestamp": "_fv_ts"} + ) + merged = result[[eid_col, event_timestamp_col]].merge( + fv_join, on=eid_col, how="left" + ) + if strict_pit: + future_mask = merged["_fv_ts"] > merged[event_timestamp_col] else: - # merge_asof path (training data) - result = result.sort_values(event_timestamp_col).reset_index( - drop=True + future_mask = pd.Series( + [False] * len(merged), index=merged.index ) - fv_df = fv_df.sort_values("event_timestamp").reset_index( - drop=True + if fv_ttl: + ttl_mask = merged["_fv_ts"] < ( + merged[event_timestamp_col] - fv_ttl ) - merge_cols = ["_fv_entity_id", "event_timestamp"] + [ - f for f in features if f in fv_df.columns - ] - fv_df_subset = fv_df[ - [c for c in merge_cols if c in fv_df.columns] - ].copy() - fv_df_subset = fv_df_subset.rename( - columns={"event_timestamp": "_fv_ts"} - ) - fv_prefix = f"__fv_{fv_name}__" - fv_df_subset = fv_df_subset.rename( - columns={ - f: f"{fv_prefix}{f}" - for f in features - if f in fv_df_subset.columns - } - ) - result = pd.merge_asof( - result, - fv_df_subset, - left_on=event_timestamp_col, - right_on="_fv_ts", - by="_fv_entity_id", - direction="backward", + bad_mask = future_mask | ttl_mask + else: + bad_mask = future_mask + for feat in features: + col = f"{proj_name}__{feat}" if full_feature_names else feat + vals = ( + merged[feat].copy() + if feat in merged.columns + else pd.Series([None] * len(merged), dtype=object) ) - if fv and fv.ttl: - cutoff = result[event_timestamp_col] - fv.ttl - stale = result["_fv_ts"] < cutoff - for feat in features: - tc = f"{fv_prefix}{feat}" - if tc in result.columns: - result.loc[stale, tc] = None + vals[bad_mask | merged["_fv_ts"].isna()] = None + result[col] = vals.values + else: + # merge_asof path (training data) + result = result.sort_values(event_timestamp_col).reset_index( + drop=True + ) + fv_df = fv_df.sort_values("event_timestamp").reset_index(drop=True) + merge_cols = [eid_col, "event_timestamp"] + [ + f for f in features if f in fv_df.columns + ] + fv_df_subset = fv_df[ + [c for c in merge_cols if c in fv_df.columns] + ].copy() + fv_df_subset = fv_df_subset.rename( + columns={"event_timestamp": "_fv_ts"} + ) + fv_prefix = f"__fv_{proj_name}__" + fv_df_subset = fv_df_subset.rename( + columns={ + f: f"{fv_prefix}{f}" + for f in features + if f in fv_df_subset.columns + } + ) + result = pd.merge_asof( + result, + fv_df_subset, + left_on=event_timestamp_col, + right_on="_fv_ts", + by=eid_col, + direction="backward", + ) + if fv_ttl: + cutoff = result[event_timestamp_col] - fv_ttl + stale = result["_fv_ts"] < cutoff for feat in features: tc = f"{fv_prefix}{feat}" - col = f"{fv_name}__{feat}" if full_feature_names else feat if tc in result.columns: - if col in result.columns: - result = result.drop(columns=[col]) - result = result.rename(columns={tc: col}) - elif col not in result.columns: - result[col] = None - result = result.drop(columns=["_fv_ts"], errors="ignore") + result.loc[stale, tc] = None + for feat in features: + tc = f"{fv_prefix}{feat}" + col = f"{proj_name}__{feat}" if full_feature_names else feat + if tc in result.columns: + if col in result.columns: + result = result.drop(columns=[col]) + result = result.rename(columns={tc: col}) + elif col not in result.columns: + result[col] = None + result = result.drop(columns=["_fv_ts"], errors="ignore") - # Drop the group's shared entity key column after all FVs processed - result = result.drop(columns=["_fv_entity_id"], errors="ignore") + result = result.drop(columns=[eid_col], errors="ignore") return result @@ -908,6 +972,11 @@ def offline_write_batch( now = datetime.now(tz=timezone.utc) + # Use the batch source name as the MongoDB discriminator so that + # data written via push/write_to_offline_store lands in the same + # collection partition as the initial ingest from create_data_source. + mongo_fv_name = feature_view.batch_source.feature_view_name + docs = [] for _, row in df.iterrows(): features: Dict[str, Any] = {} @@ -930,7 +999,7 @@ def offline_write_batch( docs.append( { "entity_id": row["_entity_id"], - "feature_view": feature_view.name, + "feature_view": mongo_fv_name, "features": features, "event_timestamp": ( row[timestamp_field].to_pydatetime() From 136dc09bf1ac73a207a2d76344c3acc13f7dfc92 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 16:11:25 -0400 Subject: [PATCH 58/76] Fix MongoDB test DataSourceCreator: implement create_logged_features_destination Return FileLoggingDestination instead of raising NotImplementedError, unblocking feature logging tests. Signed-off-by: Casey Clements --- .../feature_repos/universal/data_sources/mongodb.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py b/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py index 422cae278d2..829dcda9766 100644 --- a/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py +++ b/sdk/python/tests/universal/feature_repos/universal/data_sources/mongodb.py @@ -17,7 +17,10 @@ MongoDBOfflineStoreConfig, MongoDBSource, ) -from feast.infra.offline_stores.file_source import SavedDatasetFileStorage +from feast.infra.offline_stores.file_source import ( + FileLoggingDestination, + SavedDatasetFileStorage, +) from feast.protos.feast.types.EntityKey_pb2 import EntityKey as EntityKeyProto from feast.protos.feast.types.Value_pb2 import Value as ValueProto from feast.repo_config import FeastConfigBaseModel @@ -173,7 +176,8 @@ def create_saved_dataset_destination(self) -> SavedDatasetFileStorage: return SavedDatasetFileStorage(path=path) def create_logged_features_destination(self) -> LoggingDestination: - raise NotImplementedError("MongoDB LoggingDestination not implemented.") + d = tempfile.mkdtemp(prefix=self.project_name) + return FileLoggingDestination(path=d) def teardown(self): try: From f7e6230f17e8f7e005e25a07c49cf5633a9a48ce Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 16:22:05 -0400 Subject: [PATCH 59/76] Fix pd.isna() ValueError on list/array features in offline_write_batch pd.isna() on list/array values returns a numpy array instead of a scalar bool, causing 'truth value of an array is ambiguous' ValueError. Guard with try/except and isinstance check to handle non-scalar types. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index b5149a4002c..19a80e7cf4c 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -982,8 +982,12 @@ def offline_write_batch( features: Dict[str, Any] = {} for col in feature_cols: val = row[col] - if pd.isna(val): - continue + try: + is_na = pd.isna(val) + if isinstance(is_na, bool) and is_na: + continue + except (ValueError, TypeError): + pass # non-scalar (list/array) — not NA if hasattr(val, "item"): val = val.item() features[col] = val From 274e6f8e9b395f1a9a5edccf5bcc167952a1eb2e Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 16:41:11 -0400 Subject: [PATCH 60/76] Fix bool/int type inference order in get_table_column_names_and_types Check isinstance(v, bool) before isinstance(v, int) since bool is a subclass of int in Python. Without this, boolean features are always inferred as int64. Signed-off-by: Casey Clements --- .../offline_stores/contrib/mongodb_offline_store/mongodb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 19a80e7cf4c..e9f43ac1458 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -218,14 +218,14 @@ def get_table_column_names_and_types( features = doc.get("features", {}) if isinstance(features, dict): for k, v in features.items(): - if isinstance(v, int): + if isinstance(v, bool): + result.append((k, "bool")) + elif isinstance(v, int): result.append((k, "int64")) elif isinstance(v, float): result.append((k, "float64")) elif isinstance(v, str): result.append((k, "string")) - elif isinstance(v, bool): - result.append((k, "bool")) elif isinstance(v, list): result.append((k, "list")) elif isinstance(v, dict): From fcb92da7a0aed8efc022394f24fbfffdd7bede65 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 16:41:47 -0400 Subject: [PATCH 61/76] Fix mongodb_to_feast_value_type to accept type strings from get_table_column_names_and_types get_table_column_names_and_types produces 'int64', 'float64', 'string', 'list', 'dict' but mongodb_to_feast_value_type only accepted 'int', 'float', 'str'. Add aliases so schema inference works correctly for MongoDBSource feature views. Signed-off-by: Casey Clements --- sdk/python/feast/type_map.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 9bd6335524f..7960a3a3620 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -2060,11 +2060,16 @@ def mongodb_to_feast_value_type(type_str: str) -> ValueType: """ type_map: Dict[str, ValueType] = { "str": ValueType.STRING, + "string": ValueType.STRING, "int": ValueType.INT64, + "int64": ValueType.INT64, "float": ValueType.DOUBLE, + "float64": ValueType.DOUBLE, "bool": ValueType.BOOL, "bytes": ValueType.BYTES, "datetime": ValueType.UNIX_TIMESTAMP, + "list": ValueType.UNKNOWN, + "dict": ValueType.UNKNOWN, "list[str]": ValueType.STRING_LIST, "list[int]": ValueType.INT64_LIST, "list[float]": ValueType.DOUBLE_LIST, From 23b678847159abf3ad6db859dbb6e3f6f8f8d746 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 16:42:34 -0400 Subject: [PATCH 62/76] Sort join keys in _serialize_entity_key_from_row for consistent entity_id bytes get_historical_features sorts join keys before serialization, but _serialize_entity_key_from_row (used by offline_write_batch) did not. For compound keys in non-alphabetical order, this produced different entity_id bytes, causing written features to be unmatched on read. Signed-off-by: Casey Clements --- .../offline_stores/contrib/mongodb_offline_store/mongodb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index e9f43ac1458..f5d1bb9bfd0 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -318,7 +318,7 @@ def _serialize_entity_key_from_row( join_key_types: Dict[str, ValueType], ) -> bytes: entity_key = EntityKeyProto() - for jk in join_keys: + for jk in sorted(join_keys): val = row[jk] entity_key.join_keys.append(jk) proto_val = ValueProto() From 26b51533b43324c5449320298282e42e84a225d3 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 16:52:27 -0400 Subject: [PATCH 63/76] Resolve .secrets.baseline merge conflict with master Signed-off-by: Casey Clements --- .secrets.baseline | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index bb012ea649b..6ade5e5c610 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1539,9 +1539,5 @@ } ] }, -<<<<<<< FEAST-OfflineStore-INTPYTHON-297 "generated_at": "2026-04-23T17:55:15Z" -======= - "generated_at": "2026-04-09T03:30:18Z" ->>>>>>> master } From 5227804ed23574b09c16744ddd0bacbee0a5943b Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 17:40:32 -0400 Subject: [PATCH 64/76] Add mongodb to CI extras so pymongo is installed in CI The mongodb optional dependency group was missing from the ci extras list in pyproject.toml. This meant pymongo was never installed in CI, causing all MongoDB offline store tests to be silently skipped. Signed-off-by: Casey Clements --- pyproject.toml | 2 +- .../requirements/py3.10-ci-requirements.txt | 1389 +++++++++-------- .../py3.10-minimal-requirements.txt | 1049 +++++++------ ...y3.10-minimal-sdist-requirements-build.txt | 415 +++-- .../py3.10-minimal-sdist-requirements.txt | 1163 +++++++------- .../requirements/py3.10-requirements.txt | 805 +++++----- .../requirements/py3.11-ci-requirements.txt | 1290 ++++++++------- .../py3.11-minimal-requirements.txt | 1049 +++++++------ ...y3.11-minimal-sdist-requirements-build.txt | 413 +++-- .../py3.11-minimal-sdist-requirements.txt | 1163 +++++++------- .../requirements/py3.11-requirements.txt | 805 +++++----- .../requirements/py3.12-ci-requirements.txt | 1296 +++++++-------- .../py3.12-minimal-requirements.txt | 1043 +++++++------ ...y3.12-minimal-sdist-requirements-build.txt | 413 +++-- .../py3.12-minimal-sdist-requirements.txt | 1157 +++++++------- .../requirements/py3.12-requirements.txt | 799 +++++----- 16 files changed, 7472 insertions(+), 6779 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2e45d1820e7..f917bba2402 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -153,7 +153,7 @@ test = [ ] ci = [ - "feast[test, aws, azure, cassandra, clickhouse, couchbase, delta, docling, duckdb, elasticsearch, faiss, gcp, ge, go, grpcio, hazelcast, hbase, ibis, image, k8s, mcp, milvus, mssql, mysql, openlineage, opentelemetry, oracle, spark, trino, postgres, pytorch, qdrant, rag, ray, redis, singlestore, snowflake, sqlite_vec]", + "feast[test, aws, azure, cassandra, clickhouse, couchbase, delta, docling, duckdb, elasticsearch, faiss, gcp, ge, go, grpcio, hazelcast, hbase, ibis, image, k8s, mcp, milvus, mongodb, mssql, mysql, openlineage, opentelemetry, oracle, spark, trino, postgres, pytorch, qdrant, rag, ray, redis, singlestore, snowflake, sqlite_vec]", "build", "virtualenv==20.23.0", "dbt-artifacts-parser", diff --git a/sdk/python/requirements/py3.10-ci-requirements.txt b/sdk/python/requirements/py3.10-ci-requirements.txt index 4a5a8e1e77a..e26ccdc4a83 100644 --- a/sdk/python/requirements/py3.10-ci-requirements.txt +++ b/sdk/python/requirements/py3.10-ci-requirements.txt @@ -301,49 +301,44 @@ botocore==1.38.46 \ # moto # s3transfer # snowflake-connector-python -build==1.4.2 \ - --hash=sha256:35b14e1ee329c186d3f08466003521ed7685ec15ecffc07e68d706090bf161d1 \ - --hash=sha256:7a4d8651ea877cb2a89458b1b198f2e69f536c95e89129dbf5d448045d60db88 +build==1.4.4 \ + --hash=sha256:8c3f48a6090b39edec1a273d2d57949aaf13723b01e02f9d518396887519f64d \ + --hash=sha256:f832ae053061f3fb524af812dc94b8b84bac6880cd587630e3b5d91a6a9c1703 # via # feast (pyproject.toml) # pip-tools # singlestoredb -cassandra-driver==3.29.3 \ - --hash=sha256:064bf45d3ca87239e11168c0110676fc64f7fdbddb4bcba9be787b8ad5f6d734 \ - --hash=sha256:0785f6e0986089e922378ae3b64b5f696440aeb595fb84c2cf3ccef220c6ae91 \ - --hash=sha256:158f7e5cb894a76a592aa0ca659a8e7c2a57ef603e04c07bbbc289a70e9ac893 \ - --hash=sha256:1c241ba08473baf31a333feb59793190d01625541c2368d3bbb0f43a586f1d6a \ - --hash=sha256:26013d768b2ea4728c09144b08c0eb86ad692e85cb15f4e52e3107abca83683c \ - --hash=sha256:27adf8869937461ad08c5fefb47857532e467b408db496db4dbf8b132a4bd623 \ - --hash=sha256:281f67af1b8df88741eef551afbb49f78e4f366a7ab23e7060a1f0d6ba655752 \ - --hash=sha256:29fc241475801872dc27c3dd1a3976373536223dd4fd1c01868ff86bdbbfd48b \ - --hash=sha256:2b72312a8b62a905da6133effbba9b0731c8e30af96e10ca77fc5c34532c6827 \ - --hash=sha256:2cb72808dfc46c40a6ee352ace181ce3170adde1cfd1447da91709a8cf482e20 \ - --hash=sha256:38216e13d6f2e0d4513a5b8806e70ce4a8f28a82962793a67371582fc2c7141b \ - --hash=sha256:3f654b01d8d49f68deedfaff1edcff314e3103d29130b2a034df6c490c522351 \ - --hash=sha256:51d6a5390e2454b599500049f0a5c72aa701db155c1e542f9a1157c1c45814b1 \ - --hash=sha256:54afde4aaa5b55fbc2c075e1c55fb14a5739459428f3bb81f849ad020f7d5bcf \ - --hash=sha256:572bd5a01089ab92da12f4f52b32b878547bbc544a798d8cfd042e7fc2601c75 \ - --hash=sha256:5a0113020d86e8f61c7a2ae3d508720cd036df7462a55926b85dd97ada27e143 \ - --hash=sha256:5f9858b5ccdf75dd89c20d74474b59dd3a2e2f86c7251b310011c46acdef3874 \ - --hash=sha256:638047c1f70fb14c9d8f743931d4f4f42aff6793b47afded3097c002ef8c1165 \ - --hash=sha256:63adca0f9219be3fe8789f4aa7b77c5f6a7bf65d6442959db52c653140ca4185 \ - --hash=sha256:7552fb7189acd06161f8feac7045a387dc9e03b3b9f7dcb5675178906cee792e \ - --hash=sha256:7a2f371af54cd1d153ef373a733889ebfbcc9c30e00429fc12a2569bad9239e1 \ - --hash=sha256:84b24f69a7bbe76302330d47422a7fcc1998a6a96ffd414a795d7d95992b49cb \ - --hash=sha256:891a1b6a111a591ad9f1c9e088846848dc9e6be030a6086c8c3aa5d2d837f266 \ - --hash=sha256:96ad742f5cbfb771df512959ab5de36e248ce9aa2c487fd81c37d5c0a627c094 \ - --hash=sha256:9abedc832e9a6636741299aae46c032d8c1248b507d8cebbaa2f48ec202904bc \ - --hash=sha256:9b7032b44769c454e96aa11483bfd167a87ea341268f1075b0ff84f780c910a9 \ - --hash=sha256:c935431682557ffcd3efc1c7bcb01b0f6769a1c90751a7154d5e3c905a6a2042 \ - --hash=sha256:e1d09691d757f5b1900a98cc3b6cc7d8506683a2188c01eca86545f91edbbaf5 \ - --hash=sha256:facd488c2b9be8bffcad5903566581e96d2863d2ec4bcad7f114d1b2b2f39ad0 \ - --hash=sha256:fcf45725ae1751cb934b9b827a7d9cd899bbd09eb1ad28e2160b4584de35ba77 \ - --hash=sha256:ff6b82ee4533f6fd4474d833e693b44b984f58337173ee98ed76bce08721a636 +cassandra-driver==3.30.0 \ + --hash=sha256:0c28a8e84917acebecbaed39844047c2f135739c3627dd7b9f8541af33e11df3 \ + --hash=sha256:0f4225082a11d9529416c223553ab38a29c4e65da6646b40159c554480dc002c \ + --hash=sha256:136b46437b9902673264e101cdaab309d3e40607bff34430bda86b785badc6e4 \ + --hash=sha256:137498e2a9b6f578d1902e1af8a988e50b8fe134c76a176f1b8a774e906bc66c \ + --hash=sha256:17fb53587c9fc6a27b5c4a89b4f3d9169be43fc572d6f3f67494aa74708be936 \ + --hash=sha256:1d64cbdce764c33e284d339b9a749736d68971edf8b537888f2d13c4b0d1313f \ + --hash=sha256:212af4d8ff934c30538f4bdf7da61f14dc9a30349f6cac2161c8125e56fad928 \ + --hash=sha256:2637644eac9274e46b0c2a7f729158bdf8582b6842dc48e18297211dd3ee1fec \ + --hash=sha256:289e86c81be2543cb9055600c0819850db921e6e138a84e5c88ec160662c7207 \ + --hash=sha256:2a0679ebcfdcecb3763c690b5bc6a517e0c0803f7bc88e0a6c793e5e421b558a \ + --hash=sha256:385134eba72f048707cd800de0a61cf3c23246113edffe9bc6bc2eb86282d26b \ + --hash=sha256:5c6cbb396ad6fe456efc799d3b8b6bda360ffc06552c5be2ce1a88ac381a305c \ + --hash=sha256:61d7eeb17d8f76d5b4a9b1239145250f2a9f7bf949c30e2cc36196b5a0523ce1 \ + --hash=sha256:6a5c8982f2b9eb4e789fc12cdd930b1e1511b6d046dde31d0703f855745556a3 \ + --hash=sha256:6d449f49ce866ac20a1c3d80b1f9245ecdfd1e67b843dccd3d6eccdfe519c02e \ + --hash=sha256:7e4cfd6ec3023576ed0ffa34882d9778e4bacfd918048ae9139ccdd00628ed85 \ + --hash=sha256:83a9148d408a3dbb48ea1802d643d60fa53cd69dc7b9a244511ecf5b917e4f53 \ + --hash=sha256:8c4acd28791854c23ca68be50a7a750c9413ba80fec0ca5c27c2be05f6f3fe0a \ + --hash=sha256:8d5e3575ec01d8c043b56ff25de6f61ff4c9ed5cb3ea4c3d9df98def71ba710c \ + --hash=sha256:923a6e1c3fa5f98f846a028b1a7207ec9e7d8cfa54ea47a507d41122efa2f54f \ + --hash=sha256:c1b4aa6c7706dec839134adb6a2094d90c5f6f35efa08028ed6aae6e67c8643e \ + --hash=sha256:c64e20bf46b49f8ef64569208d4a395b0928c27d5960559922a2d13471924d0d \ + --hash=sha256:d2f9e00127f70dff42d4ef932df8a6b81170c2861d4e75c8b13f4b4816b4450c \ + --hash=sha256:d73c0429813045ba86b92fc033fbcfd495aa10e9d4a40fe30b6e9dfe8b5d3ab4 \ + --hash=sha256:e12dfcd3f0074c16f4bfe650242edb406b935864373ae86160e09e3f5e437e84 \ + --hash=sha256:ff2e9fbdc1be54c1d041ea3f7d09812442f334be14bb5ad7aede175544765d25 # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # clickhouse-connect # docling @@ -580,9 +575,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -851,56 +846,56 @@ coverage[toml]==7.13.5 \ --hash=sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0 \ --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f # via pytest-cov -cryptography==46.0.6 \ - --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ - --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ - --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ - --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ - --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ - --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ - --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ - --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ - --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ - --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ - --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ - --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ - --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ - --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ - --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ - --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ - --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ - --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ - --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ - --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ - --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ - --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ - --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ - --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ - --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ - --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ - --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ - --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ - --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ - --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ - --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ - --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ - --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ - --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ - --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ - --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ - --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ - --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ - --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ - --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 +cryptography==47.0.0 \ + --hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \ + --hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \ + --hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \ + --hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \ + --hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \ + --hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \ + --hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \ + --hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \ + --hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \ + --hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \ + --hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \ + --hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \ + --hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \ + --hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \ + --hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \ + --hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \ + --hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \ + --hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \ + --hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \ + --hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \ + --hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \ + --hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \ + --hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \ + --hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \ + --hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \ + --hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \ + --hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \ + --hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \ + --hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \ + --hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \ + --hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \ + --hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \ + --hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \ + --hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \ + --hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \ + --hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \ + --hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \ + --hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \ + --hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \ + --hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \ + --hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \ + --hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \ + --hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \ + --hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \ + --hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \ + --hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \ + --hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \ + --hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \ + --hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736 # via # feast (pyproject.toml) # azure-identity @@ -930,9 +925,9 @@ db-dtypes==1.5.1 \ # via # google-cloud-bigquery # pandas-gbq -dbt-artifacts-parser==0.13.0 \ - --hash=sha256:304f2b857650566fed4ed8b976ed3582332eda3cedfe7167158dbbcfced3fe47 \ - --hash=sha256:55498e8bd0d9064d56617f9c714ced8607d94ccb61e70d4b49dcfd8a28a030d8 +dbt-artifacts-parser==0.13.1 \ + --hash=sha256:c341730fa34ebb38cc7d2de0282e8b713e2fc65fc6577f0d944f8abee8949dc4 \ + --hash=sha256:c7a3c4e309ae2d7d566a615e92043b0d346a77998203b0cc466234717b806e40 # via feast (pyproject.toml) debugpy==1.8.20 \ --hash=sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad \ @@ -985,6 +980,10 @@ deltalake==0.25.5 \ --hash=sha256:cb1c7e826fd7c3bdd3676c7471d3b551e1a3674e44cd8e3747a0017a2c0292b7 \ --hash=sha256:e8f0d24bf64455f702da8402307b22e01f91e0f76694f7c5e33c9513011e8d29 # via feast (pyproject.toml) +deprecated==1.3.1 \ + --hash=sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f \ + --hash=sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223 + # via cassandra-driver deprecation==2.1.0 \ --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a @@ -1000,6 +999,12 @@ distlib==0.4.0 \ --hash=sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 \ --hash=sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d # via virtualenv +dnspython==2.8.0 \ + --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \ + --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f + # via + # feast (pyproject.toml) + # pymongo docker==7.1.0 \ --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \ --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0 @@ -1008,16 +1013,16 @@ docling==2.27.0 \ --hash=sha256:1288ed75b27e33bf94daff34faffc6d11b7d7ccc13e3df84fb24adad3991f72d \ --hash=sha256:faba35662612a2c687a3a463e501d95f645316436084af92a0442ce162429a3d # via feast (pyproject.toml) -docling-core[chunking]==2.71.0 \ - --hash=sha256:4761857816853b2b35263b5b4518e1ea6214e0565db0bbf1d929fb976665d1a0 \ - --hash=sha256:4caa9f50c68b9dd332584ae16170b36db05d773532b14d7078b580d89d8bd2a4 +docling-core[chunking]==2.74.1 \ + --hash=sha256:46bf298686f2c51ddd69b6935a27dff1cc80838f2f5f1a8823492d99cf1a357b \ + --hash=sha256:e6464078012b3d45f4e0accd101fcb277063903f355eabbb9aee8de00527a789 # via # docling # docling-ibm-models # docling-parse -docling-ibm-models==3.13.0 \ - --hash=sha256:a11acc6034b06e0bed8dc0ca1fa700615b8246eacce411619168e1f6562b0d0d \ - --hash=sha256:f402effae8a63b0e5c3b5ce13120601baa2cd8098beef1d53ab5a056443758d3 +docling-ibm-models==3.13.2 \ + --hash=sha256:195e02dd119df34d2ce5f76ac614da82825851013e4898db7b0468cdf8740a3d \ + --hash=sha256:5fa0838bf15a4e06d2fcb686d756a6f4c329ea0a8820d085f06d07abe96269ed # via docling docling-parse==4.7.3 \ --hash=sha256:1790e7e4ae202d67875c1c48fd6f8ef5c51d10b0c23157e4989b8673f2f31308 \ @@ -1052,42 +1057,42 @@ docutils==0.19 \ --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc # via sphinx -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ @@ -1154,9 +1159,9 @@ faiss-cpu==1.10.0 \ --hash=sha256:e71f7e24d5b02d3a51df47b77bd10f394a1b48a8331d5c817e71e9e27a8a75ac \ --hash=sha256:f71c5860c860df2320299f9e4f2ca1725beb559c04acb1cf961ed24e6218277a # via feast (pyproject.toml) -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -1168,9 +1173,9 @@ fastjsonschema==2.21.2 \ --hash=sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463 \ --hash=sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de # via nbformat -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via # datasets # huggingface-hub @@ -1334,9 +1339,9 @@ geomet==1.1.0 \ --hash=sha256:4372fe4e286a34acc6f2e9308284850bd8c4aa5bc12065e2abbd4995900db12f \ --hash=sha256:51e92231a0ef6aaa63ac20c443377ba78a303fd2ecd179dc3567de79f3c11605 # via cassandra-driver -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -1346,9 +1351,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-datastore # google-cloud-storage # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -1828,13 +1833,13 @@ ibis-framework[duckdb, mssql, oracle]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -identify==2.6.18 \ - --hash=sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd \ - --hash=sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737 +identify==2.6.19 \ + --hash=sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a \ + --hash=sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842 # via pre-commit -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1856,9 +1861,9 @@ importlib-metadata==9.0.0 \ # via # build # dask -importlib-resources==6.5.2 \ - --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ - --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec +importlib-resources==7.1.0 \ + --hash=sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708 \ + --hash=sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1 # via happybase iniconfig==2.3.0 \ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ @@ -1973,9 +1978,9 @@ jupyter-core==5.9.1 \ # nbclient # nbconvert # nbformat -jupyter-events==0.12.0 \ - --hash=sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb \ - --hash=sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b +jupyter-events==0.12.1 \ + --hash=sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf \ + --hash=sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3 # via jupyter-server jupyter-lsp==2.3.1 \ --hash=sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81 \ @@ -2012,9 +2017,9 @@ jupyterlab-widgets==3.0.16 \ --hash=sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0 \ --hash=sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8 # via ipywidgets -jwcrypto==1.5.6 \ - --hash=sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789 \ - --hash=sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039 +jwcrypto==1.5.7 \ + --hash=sha256:70204d7cca406eda8c82352e3c41ba2d946610dafd19e54403f0a1f4f18633c6 \ + --hash=sha256:729463fefe28b6de5cf1ebfda3e94f1a1b41d2799148ef98a01cb9678ebe2bb0 # via python-keycloak kubernetes==35.0.0 \ --hash=sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d \ @@ -2024,9 +2029,9 @@ lark==1.3.1 \ --hash=sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905 \ --hash=sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12 # via rfc3987-syntax -latex2mathml==3.79.0 \ - --hash=sha256:11bde318c2d2d6fcdd105a07509d867cee2208f653278eb80243dec7ea77a0ce \ - --hash=sha256:9f10720d4fcf6b22d1b81f6628237832419a7a29783c13aa92fa8d680165e63d +latex2mathml==3.81.0 \ + --hash=sha256:4b959cdc3cac8686bc0e3e5aece8127dfb1b81ca1241bed8e00ef31b82bb4022 \ + --hash=sha256:d317710393fe20579aea39cfe8928fa2ad9b8780896e585326c75e89c1d1d1a4 # via docling-core lazy-loader==0.5 \ --hash=sha256:717f9179a0dbed357012ddad50a5ad3d5e4d9a0b8712680d4e687f5e6e6ed9b3 \ @@ -2501,9 +2506,9 @@ mpmath==1.3.0 \ --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c # via sympy -msal==1.35.1 \ - --hash=sha256:70cac18ab80a053bff86219ba64cfe3da1f307c74b009e2da57ef040eb1b5656 \ - --hash=sha256:8f4e82f34b10c19e326ec69f44dc6b30171f2f7098f3720ea8a9f0c11832caa3 +msal==1.36.0 \ + --hash=sha256:36ecac30e2ff4322d956029aabce3c82301c29f0acb1ad89b94edcabb0e58ec4 \ + --hash=sha256:3f6a4af2b036b476a4215111c4297b4e6e236ed186cd804faefba23e4990978b # via # azure-identity # msal-extensions @@ -2789,9 +2794,9 @@ nbclient==0.10.4 \ --hash=sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9 \ --hash=sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440 # via nbconvert -nbconvert==7.17.0 \ - --hash=sha256:1b2696f1b5be12309f6c7d707c24af604b87dfaf6d950794c7b07acab96dda78 \ - --hash=sha256:4f99a63b337b9a23504347afdab24a11faa7d86b405e5c8f9881cd313336d518 +nbconvert==7.17.1 \ + --hash=sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2 \ + --hash=sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8 # via jupyter-server nbformat==5.10.4 \ --hash=sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a \ @@ -2942,8 +2947,8 @@ opencv-python-headless==4.13.0.92 \ --hash=sha256:a7cf08e5b191f4ebb530791acc0825a7986e0d0dee2a3c491184bd8599848a4b \ --hash=sha256:eb60e36b237b1ebd40a912da5384b348df8ed534f6f644d8e0b4f103e272ba7d # via easyocr -openlineage-python==1.45.0 \ - --hash=sha256:cf66e7d517d3c8b510b39ad646d8fd0ca2f0cc92d7d6d601d93b2a859783f380 +openlineage-python==1.46.0 \ + --hash=sha256:f6228a01d34990e76ede5b55b3f99169e54e2e624814c4493f064b9cb1bfba37 # via feast (pyproject.toml) openpyxl==3.1.5 \ --hash=sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2 \ @@ -3062,9 +3067,9 @@ overrides==7.7.0 \ --hash=sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a \ --hash=sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49 # via jupyter-server -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # accelerate # build @@ -3350,9 +3355,9 @@ pre-commit==3.3.1 \ --hash=sha256:218e9e3f7f7f3271ebc355a15598a4d3893ad9fc7b57fe446db75644543323b9 \ --hash=sha256:733f78c9a056cdd169baa6cd4272d51ecfda95346ef8a89bf93712706021b907 # via feast (pyproject.toml) -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via # feast (pyproject.toml) # jupyter-server @@ -3658,57 +3663,57 @@ py4j==0.10.9.9 \ --hash=sha256:c7c26e4158defb37b0bb124933163641a2ff6e3a3913f7811b0ddbe07ed61533 \ --hash=sha256:f694cad19efa5bd1dee4f3e5270eb406613c974394035e5bfc4ec1aba870b879 # via pyspark -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -3820,9 +3825,9 @@ pycryptodome==3.23.0 \ --hash=sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be \ --hash=sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7 # via minio -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # dbt-artifacts-parser @@ -3836,134 +3841,134 @@ pydantic==2.12.5 \ # mcp # pydantic-settings # qdrant-client -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # docling + # docling-core # fastapi-mcp # mcp pydata-google-auth==1.9.1 \ @@ -3996,6 +4001,79 @@ pymilvus==2.5.18 \ --hash=sha256:1b78badcfa8d62db7d0b29193fc0422e4676873ff1c745a9d75c2c885d7a7e32 \ --hash=sha256:9e517076068e98dac51c018bc0dfe1f651d936154e2e2d9ad6c7b3dab1164e2d # via feast (pyproject.toml) +pymongo==4.17.0 \ + --hash=sha256:0ff6bd2f735ab5356541e3e57d5b7dbfbc3f2ee1ccb10b6b0f82d58af69d1d8e \ + --hash=sha256:1175563375d682260f613a96fb7a53dce746ed752bfd924eab61de3bc5bfde34 \ + --hash=sha256:1195370a77baf003b59b10e91ecc4706297197f0dd9d29c840cc556dc08f7cee \ + --hash=sha256:12c4fded3a9f1d6a687e36ebd384ac6d00b9b00de1969aa74048e7051ec2a713 \ + --hash=sha256:15d3f3d732aecac1f8d481bde4029755615639bd3076f258a2147210aec8515a \ + --hash=sha256:20323b0b1c1d33770ad1fc68d429c757734ce9ad3594421c3d6618f10572b1b9 \ + --hash=sha256:2a0d5ac205728c86e0a02192f1aa5f865b0d7d51f8df6101c01a69a7fc620d72 \ + --hash=sha256:2db66aa8dd253a0fc1fad3b0d23d5b3993f7ebde02fbbd7727128debf2853675 \ + --hash=sha256:2e190827834fce70ecdf9d46796c6dbc0ce08ea87dc2ff5bc6f3f5579b605cb9 \ + --hash=sha256:320b34457b20bbcc79997801f95d25ce00472915ca5241167242b42c4359e027 \ + --hash=sha256:3689ea34f6b647c7d1e7bdc60fcfb214b2789ed1359a7fb96569c69f50e5f18f \ + --hash=sha256:37a8385c29881b43eab31f584100fa0eaddedd5607adf010147ba1810118be90 \ + --hash=sha256:3987e96e7c7be4083d42e8ac2cc6c0d5b78db9973c90fce42ae800b616ca6b20 \ + --hash=sha256:4141e6c6a339789b2974efa00ecd9409101672d77a0e3ee2cc3839eedf8ec4df \ + --hash=sha256:422fa50d7d7f5c22ea0953554396c9ef95684a2d775f860bd75a7b510538dfca \ + --hash=sha256:47b021363cd923ace5edc7a1d63c0ff8a6d9d43859b8a1ba23645f5afae63221 \ + --hash=sha256:485c8a8eaa4c739f00a331fc73757898ee7c092c214a79e63866ff76aaf282ff \ + --hash=sha256:48bbc576677b50af043df870d84ded67cc3a9b4aa7553201beef4da5dc050a0a \ + --hash=sha256:4ae22fafca69dd3c78261969e999782ac5fc23b76cf8cccfbc3707982a74cc3d \ + --hash=sha256:50e8f8e23c6df7c6d6929f5e734980b227706e73ee847517c9ba5af90f7fc466 \ + --hash=sha256:51e1915761f65f2aaabd0ba691a31d56551d3f19d1263c2d6bf261730603de5f \ + --hash=sha256:5376ad67bb30ae910d83affcf997f706d9dee37e8b5dad8b6fedb0626e262d85 \ + --hash=sha256:5960519b4d7168f1ecdd3ea10c81b2aedeb9423651aca953cfbc8e76705d3b38 \ + --hash=sha256:5a5de048e6da5c18e27cc2437e8c15b3b0cdc8385c15b41178b0caa3322a09c2 \ + --hash=sha256:5ab3b8ff79e0dfc49b68f3c925e8cc735ea95c60efaed84cfe75692dffcaac2a \ + --hash=sha256:64837adbbd72073301af51bb0fc80e3d7707fe5527cea1033ba0320f0b2f881b \ + --hash=sha256:6877214bff5f06f6884a9fc8d9016a4a7a5f51f537f5c51ac3a576f93e7dfb32 \ + --hash=sha256:68fca71e05ee5da23a8d73cee8379dfb3d26e609a377cae731d742771ed96946 \ + --hash=sha256:6c5f62862d0f87be481fa1fe8cb811994486773c94a2b61e509285e3f2890763 \ + --hash=sha256:6fe0de9d0f6791abce3471230b32b4817bf89d27b1182b6a550e1ec0fa72aa9a \ + --hash=sha256:70ffa08ba641468cc068cf46c06b34f01a8ce3489f6411309fcb5ceabe6b2fc0 \ + --hash=sha256:757f2a4c0c2c46cab87df0333681ce69e86c9d5b45bc5203ceba5410b3489e59 \ + --hash=sha256:75bc3aa5b94fdb7138d357ec6ca61cd97e0c79f4f7f0bd3efe9639b15cc50942 \ + --hash=sha256:77aa4bc164b4de60d5db193b322f0f5b6ead716e831031bfdef8e8bd92205556 \ + --hash=sha256:7db10678814cdf7ea39fd308c6f41395cfa7b29d904bcd7895288963d8f892ba \ + --hash=sha256:809ec74de3b9148ae43fa8df9faf53470f511c8d384f13b99d6f671f2a379f15 \ + --hash=sha256:8446ff4bfcb6ec2a2e50998c860986a1e992136f998b7f53e7a717fb8aa5a0b9 \ + --hash=sha256:8a1be016198a03fd7727cdd55998964bfa4e5a6fd9733c8e95830628cef34d29 \ + --hash=sha256:8e97e03fa13327c87e3fdc5656acd01e71817f0c1dc3221cd8f30de136bf4ec3 \ + --hash=sha256:93641192644fa1ee0f34030e774fd31022a27ad11ba22cb1716142231524f8bd \ + --hash=sha256:9543d8f84c2e5608565c08ac679774811e6730770d8a645439b073422a4276fb \ + --hash=sha256:9828485f72f63c7d802e0ec41f71906f633c2692621ab3af55ca990186b091b1 \ + --hash=sha256:9eb5d63a3c518cb0804ed678f5e2b875af032d89a7cf57a57360322cf6a4d222 \ + --hash=sha256:a431b737816bf4cddd4fa0fcef04e424ad36b7692734a64150f872fb8f3208be \ + --hash=sha256:a8f9c40a09bb7d4b9fc8b1da65ecf6efa79bda5cb2756f39d9b6940fac1d19ae \ + --hash=sha256:addd0498ebbdc6354227f6ed457ed9fce442d48a3bb30d5b5bad33e104996561 \ + --hash=sha256:b24598dc3c2feccbc83b43044be48145a0dc4f9bee49ef923e3d707d54a55d85 \ + --hash=sha256:b2dfcc795f5b9fedbe179a11fdf6051581479d196582a3fe819a92a00e9b9969 \ + --hash=sha256:b4384700cffc3f1dd98e088bc0072dedf6d7d68a230bb4b972665cf69c071c1e \ + --hash=sha256:b93b22eedc62598cf5ee9d8c8007a8e9121c50fd88137012d8985500e9dc3151 \ + --hash=sha256:ba2195d4f386f839a52a23ea1cfd60ffaaba78a3d7841db51b7e433001139918 \ + --hash=sha256:bb3ebc86782049f6928dcc583008287cb1c17d463501c94a620f035f5b4fd463 \ + --hash=sha256:bd835cdb37a1adec359dd072c24f8bb14809e2644fde86fab4ee2fc9719b9483 \ + --hash=sha256:c2292144505fb12156b981bd440f3dc994a883da06ac726c0c8692ccdbc1c510 \ + --hash=sha256:c4979e7e8887862bbb44d203f00cc8263a3f27237876fa691b6beba23e40e6d8 \ + --hash=sha256:c5c8e180cb2cabe37300e1e36c60aa4f2ff956cc579f0142135a5d2cba252243 \ + --hash=sha256:c797f8a80957134f6dd9690367a0f8f5906d672119af2c6aa55f0c527b656bed \ + --hash=sha256:c9786665926a09630c5d420c79762cfadbff35a9438bcbc4c81a9fb5ab9228b7 \ + --hash=sha256:cee36b3c0d0354f880fa7a7fdcdaf2bb5e542c2281e25c1bfadf8cfe21eba7d2 \ + --hash=sha256:d53ffa94b2340dbf6b055e09a0090618c60482c158ecfc9565642fc996bf0944 \ + --hash=sha256:df4a644af9ae132d4bfdb2e9516ea51a615fd881caddfbfbd071cf1354844479 \ + --hash=sha256:dff3de1294fbbc1db0ba6b511f77b8e540601d092538a31312e99c8a91a78b1e \ + --hash=sha256:e46767f28dea610e02edf6c5d956ce615c3c7790ea396660b9b1efd5c5ead2e0 \ + --hash=sha256:e4fab10f8403169ce92f3cea921609d9ee81107306caae06c08f592d4b8ad2b5 \ + --hash=sha256:e537e95514dae1aaa718f481ec03151a0f0394bcd05f1322896d8fc1330cb729 \ + --hash=sha256:e68c76b84e0c132d9dbf9307f12ff8185702328187a87b9aca8c941303873433 \ + --hash=sha256:e816db649ba5d7de0568cf3a9f287a9dc9aad21cf0ca667ab156a7ef47fca0b0 \ + --hash=sha256:f09645e0ce4e3825fa0baa8254064a716ed0be33f78feeedd4731016cb8aaa17 \ + --hash=sha256:f3ee3d241ed77a4fc99ce3cff3b289c3ebce37f61fdd7349d3592c23b82c8784 \ + --hash=sha256:faf03e4c2aafd6de626dbd30ba246d369ae33f47f10629d1bbe40f72115027a6 \ + --hash=sha256:ff5aa3f1c7e3f08eb0e7a016c91ba468b1850ccfd63d9b1f12f56350f4974cef + # via feast (pyproject.toml) pymssql==2.3.2 \ --hash=sha256:06883bc9bdb297ae9132d9371b5b1a3a223c8f93dd6a87d1c112c6a688f26d53 \ --hash=sha256:0768d90f96ae3267d7561d3bcfe94dd671d107489e870388b12570c3debbc552 \ @@ -4128,9 +4206,9 @@ pyodbc==5.3.0 \ # via # feast (pyproject.toml) # ibis-framework -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python pyparsing==3.3.2 \ --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ @@ -4356,9 +4434,9 @@ python-keycloak==4.2.2 \ --hash=sha256:1d43a1accd4a038ed39317fcb3eb78211df6c75bbcbc4c482c99ee76327136f2 \ --hash=sha256:5137fd87c69031a372a578df96bae96b9aead2c9dad976613bc978e9e0246a1e # via feast (pyproject.toml) -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp python-pptx==1.0.2 \ --hash=sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba \ @@ -4565,22 +4643,25 @@ qdrant-client==1.17.1 \ --hash=sha256:22f990bbd63485ed97ba551a4c498181fcb723f71dcab5d6e4e43fe1050a2bc0 \ --hash=sha256:6cda4064adfeaf211c751f3fbc00edbbdb499850918c7aff4855a9a759d56cbd # via feast (pyproject.toml) -ray==2.54.1 \ - --hash=sha256:054985194bd32f4464c93f9318d247fac61e1f32ac221565ecfdc81ab8c75d0b \ - --hash=sha256:0c3ae2943176e7b239c78b825a5b2bf4135d90280083a0e19c0a75a5db4d836f \ - --hash=sha256:2766f0230806480c38a9a94502087f1d4aea919f38521a28781690613b0290a4 \ - --hash=sha256:2ea650e648acc6e76edd98c694657fd1fcb1cd97700d944a7d20da90269e9810 \ - --hash=sha256:4c6f7e23dda62a32f94083141c3f97e9c4246e3ae4ae2bc488bcd8fd0311f54a \ - --hash=sha256:512587412e2f5e1753adabfdfa4dd9cff1dc509601e36fd5fab671e448ae4dac \ - --hash=sha256:6425f15cfe6a298366b53c8658350f94ced2c548802ca3b69f94b87db16e97c5 \ - --hash=sha256:645ebfb73cfd32bd510a05ed9f2738a18d6db69929cae9701d749f2740dbfd9a \ - --hash=sha256:673a895c0c4a716ed772552baa3f5b8d7d1f7a4b34e04787fdfe6fe3049ed0d8 \ - --hash=sha256:86c51eafd3e84dad59c1ef4cf97b3ac8c088af0705782ee915e31bca5880597a \ - --hash=sha256:c0240496af274af7cd3b1b1d015f23b88e5fdafe59bfdc040e5f229e0aff5dff \ - --hash=sha256:cd452b61ae2e0daf9271f5a554614397429cc2731681bae10fe72316dadc2749 \ - --hash=sha256:d05f477d1518a00fd5880644e889a7a3eaf64ae5d1f8f239a682d052ad2a383d \ - --hash=sha256:e095dfe9c521a04e5930520b4a82ea82d61903d4cd2f3270fbc5dfbdb41b9c72 \ - --hash=sha256:ea90bed0110e0ce3ff6571e7a0c800920a3c6d299d29b8eac020dac362667169 +ray==2.55.1 \ + --hash=sha256:0053fd5b400f7ac56263aa1bbd3d68fb79341b08b8dc697c88782d5aca7b3ed4 \ + --hash=sha256:0ea2f670a7725833ad2333a8c46ab69865ad06c8e5de9f65695e0f8f35331cec \ + --hash=sha256:137f9006eee28caab8260803cca314f37bbda3fc94fdfa31c770b5d019626ad8 \ + --hash=sha256:1380e043eb57cde69b7e9199c6f2558ceeb8f0fc41c97d1d5e50ea042115f302 \ + --hash=sha256:156ed3e72ad95b645d2006cd71a8dddbcc89b56bfc00027f6225adf78bd9cb74 \ + --hash=sha256:263705f6bab29e7622a94f82da25fd7f9cead76cdf89a07aab28f79cdf8f9d95 \ + --hash=sha256:26541f69bb55607ef8335baac75b2ed12ff2ce02d56313219b29eda003039221 \ + --hash=sha256:2d5786661e192148719accc959def6cdcabd7a24cd9008005bf3d0e3c8cfd529 \ + --hash=sha256:4e618d61e1b14b6fde9a586151f3fd9d435b0b85048b997bcaa7f4a533747b2b \ + --hash=sha256:5e56d2e8f304cafe990c198a2b894f5b813de018998cd7212869201f6dc17cff \ + --hash=sha256:86e618e9ad8c6a24331c788eb599cee9838a62d2e10dfca0227743be06cf551c \ + --hash=sha256:9ad56704c8bd7e92130162f9c58e4ef473609515637673d5a36e761f95335206 \ + --hash=sha256:b062045c64c2bce39a51661624f7292c7bbf30f2a9d878627aae31d46da5712d \ + --hash=sha256:b415d590e062f248907e0fe42994943f11726b7178fcf4b1cf5546721fb1a5f8 \ + --hash=sha256:baf2ec89df7838cabdef493ff9bdbec1e6a6452f8bc696ad0c1b8a6198721745 \ + --hash=sha256:bb49fbbe53a1d931e1f92d17f9271338f0b738885f8f70b7f531aa33f019d8af \ + --hash=sha256:d5382da181c03ee2f502ef46cf0ae4bbc30157b5bd9a67d7651f6a272528a85a \ + --hash=sha256:f9844a9272ef2e6eb5771025866072cf4234cf4c7cc1a31e235b7de7111864be # via feast (pyproject.toml) redis==4.6.0 \ --hash=sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d \ @@ -4593,121 +4674,121 @@ referencing==0.37.0 \ # jsonschema # jsonschema-specifications # jupyter-events -regex==2026.3.32 \ - --hash=sha256:03c2ebd15ff51e7b13bb3dc28dd5ac18cd39e59ebb40430b14ae1a19e833cff1 \ - --hash=sha256:09e26cad1544d856da85881ad292797289e4406338afe98163f3db9f7fac816c \ - --hash=sha256:0cec365d44835b043d7b3266487797639d07d621bec9dc0ea224b00775797cc1 \ - --hash=sha256:0d7855f5e59fcf91d0c9f4a51dc5d8847813832a2230c3e8e35912ccf20baaa2 \ - --hash=sha256:0f21ae18dfd15752cdd98d03cbd7a3640be826bfd58482a93f730dbd24d7b9fb \ - --hash=sha256:10fb2aaae1aaadf7d43c9f3c2450404253697bf8b9ce360bd5418d1d16292298 \ - --hash=sha256:110ba4920721374d16c4c8ea7ce27b09546d43e16aea1d7f43681b5b8f80ba61 \ - --hash=sha256:12917c6c6813ffcdfb11680a04e4d63c5532b88cf089f844721c5f41f41a63ad \ - --hash=sha256:18eb45f711e942c27dbed4109830bd070d8d618e008d0db39705f3f57070a4c6 \ - --hash=sha256:1a6ac1ed758902e664e0d95c1ee5991aa6fb355423f378ed184c6ec47a1ec0e9 \ - --hash=sha256:1ca02ff0ef33e9d8276a1fcd6d90ff6ea055a32c9149c0050b5b67e26c6d2c51 \ - --hash=sha256:1cb22fa9ee6a0acb22fc9aecce5f9995fe4d2426ed849357d499d62608fbd7f9 \ - --hash=sha256:1e0f6648fd48f4c73d801c55ab976cd602e2da87de99c07bff005b131f269c6a \ - --hash=sha256:245667ad430745bae6a1e41081872d25819d86fbd9e0eec485ba00d9f78ad43d \ - --hash=sha256:2820d2231885e97aff0fcf230a19ebd5d2b5b8a1ba338c20deb34f16db1c7897 \ - --hash=sha256:2c8d402ea3dfe674288fe3962016affd33b5b27213d2b5db1823ffa4de524c57 \ - --hash=sha256:2dcca2bceb823c9cc610e57b86a265d7ffc30e9fe98548c609eba8bd3c0c2488 \ - --hash=sha256:2ffbadc647325dd4e3118269bda93ded1eb5f5b0c3b7ba79a3da9fbd04f248e9 \ - --hash=sha256:34c905a721ddee0f84c99e3e3b59dd4a5564a6fe338222bc89dd4d4df166115c \ - --hash=sha256:3c054e39a9f85a3d76c62a1d50c626c5e9306964eaa675c53f61ff7ec1204bbb \ - --hash=sha256:3c0bbfbd38506e1ea96a85da6782577f06239cb9fcf9696f1ea537c980c0680b \ - --hash=sha256:3e221b615f83b15887636fcb90ed21f1a19541366f8b7ba14ba1ad8304f4ded4 \ - --hash=sha256:3ea568832eca219c2be1721afa073c1c9eb8f98a9733fdedd0a9747639fc22a5 \ - --hash=sha256:3f5747501b69299c6b0b047853771e4ed390510bada68cb16da9c9c2078343f7 \ - --hash=sha256:462a041d2160090553572f6bb0be417ab9bb912a08de54cb692829c871ee88c1 \ - --hash=sha256:4bc32b4dbdb4f9f300cf9f38f8ea2ce9511a068ffaa45ac1373ee7a943f1d810 \ - --hash=sha256:4d082be64e51671dd5ee1c208c92da2ddda0f2f20d8ef387e57634f7e97b6aae \ - --hash=sha256:4f9ae4755fa90f1dc2d0d393d572ebc134c0fe30fcfc0ab7e67c1db15f192041 \ - --hash=sha256:51a93452034d671b0e21b883d48ea66c5d6a05620ee16a9d3f229e828568f3f0 \ - --hash=sha256:51fb7e26f91f9091fd8ec6a946f99b15d3bc3667cb5ddc73dd6cb2222dd4a1cc \ - --hash=sha256:5336b1506142eb0f23c96fb4a34b37c4fefd4fed2a7042069f3c8058efe17855 \ - --hash=sha256:567b57eb987547a23306444e4f6f85d4314f83e65c71d320d898aa7550550443 \ - --hash=sha256:5aa78c857c1731bdd9863923ffadc816d823edf475c7db6d230c28b53b7bdb5e \ - --hash=sha256:5bf2f3c2c5bd8360d335c7dcd4a9006cf1dabae063ee2558ee1b07bbc8a20d88 \ - --hash=sha256:5c35d097f509cf7e40d20d5bee548d35d6049b36eb9965e8d43e4659923405b9 \ - --hash=sha256:5d86e3fb08c94f084a625c8dc2132a79a3a111c8bf6e2bc59351fa61753c2f6e \ - --hash=sha256:6062c4ef581a3e9e503dccf4e1b7f2d33fdc1c13ad510b287741ac73bc4c6b27 \ - --hash=sha256:6128dd0793a87287ea1d8bf16b4250dd96316c464ee15953d5b98875a284d41e \ - --hash=sha256:631f7d95c83f42bccfe18946a38ad27ff6b6717fb4807e60cf24860b5eb277fc \ - --hash=sha256:66a5083c3ffe5a5a95f8281ea47a88072d4f24001d562d1d9d28d4cdc005fec5 \ - --hash=sha256:66d3126afe7eac41759cd5f0b3b246598086e88e70527c0d68c9e615b81771c4 \ - --hash=sha256:67015a8162d413af9e3309d9a24e385816666fbf09e48e3ec43342c8536f7df6 \ - --hash=sha256:6980ceb5c1049d4878632f08ba0bf7234c30e741b0dc9081da0f86eca13189d3 \ - --hash=sha256:69a847a6ffaa86e8af7b9e7037606e05a6f663deec516ad851e8e05d9908d16a \ - --hash=sha256:6ada7bd5bb6511d12177a7b00416ce55caee49fbf8c268f26b909497b534cacb \ - --hash=sha256:70c634e39c5cda0da05c93d6747fdc957599f7743543662b6dbabdd8d3ba8a96 \ - --hash=sha256:7cdd508664430dd51b8888deb6c5b416d8de046b2e11837254378d31febe4a98 \ - --hash=sha256:844d88509c968dd44b30daeefac72b038b1bf31ac372d5106358ab01d393c48b \ - --hash=sha256:847087abe98b3c1ebf1eb49d6ef320dbba75a83ee4f83c94704580f1df007dd4 \ - --hash=sha256:85c9b0c131427470a6423baa0a9330be6fd8c3630cc3ee6fdee03360724cbec5 \ - --hash=sha256:879ae91f2928a13f01a55cfa168acedd2b02b11b4cd8b5bb9223e8cde777ca52 \ - --hash=sha256:887a9fa74418d74d645281ee0edcf60694053bd1bc2ebc49eb5e66bfffc6d107 \ - --hash=sha256:88ebc0783907468f17fca3d7821b30f9c21865a721144eb498cb0ff99a67bcac \ - --hash=sha256:89e50667e7e8c0e7903e4d644a2764fffe9a3a5d6578f72ab7a7b4205bf204b7 \ - --hash=sha256:8a4a3189a99ecdd1c13f42513ab3fc7fa8311b38ba7596dd98537acb8cd9acc3 \ - --hash=sha256:8aaf8ee8f34b677f90742ca089b9c83d64bdc410528767273c816a863ed57327 \ - --hash=sha256:8e4c8fa46aad1a11ae2f8fcd1c90b9d55e18925829ac0d98c5bb107f93351745 \ - --hash=sha256:8fc918cd003ba0d066bf0003deb05a259baaaab4dc9bd4f1207bbbe64224857a \ - --hash=sha256:8fe14e24124ef41220e5992a0f09432f890037df6f93fd3d6b7a0feff2db16b2 \ - --hash=sha256:918db4e34a7ef3d0beee913fa54b34231cc3424676f1c19bdb85f01828d3cd37 \ - --hash=sha256:987cdfcfb97a249abc3601ad53c7de5c370529f1981e4c8c46793e4a1e1bfe8e \ - --hash=sha256:9b9118a78e031a2e4709cd2fcc3028432e89b718db70073a8da574c249b5b249 \ - --hash=sha256:9cf7036dfa2370ccc8651521fcbb40391974841119e9982fa312b552929e6c85 \ - --hash=sha256:a094e9dcafedfb9d333db5cf880304946683f43a6582bb86688f123335122929 \ - --hash=sha256:a416ee898ecbc5d8b283223b4cf4d560f93244f6f7615c1bd67359744b00c166 \ - --hash=sha256:a5d88fa37ba5e8a80ca8d956b9ea03805cfa460223ac94b7d4854ee5e30f3173 \ - --hash=sha256:ace48c5e157c1e58b7de633c5e257285ce85e567ac500c833349c363b3df69d4 \ - --hash=sha256:ad5c53f2e8fcae9144009435ebe3d9832003508cf8935c04542a1b3b8deefa15 \ - --hash=sha256:ad8d372587e659940568afd009afeb72be939c769c552c9b28773d0337251391 \ - --hash=sha256:b193ed199848aa96618cd5959c1582a0bf23cd698b0b900cb0ffe81b02c8659c \ - --hash=sha256:b2e9c2ea2e93223579308263f359eab8837dc340530b860cb59b713651889f14 \ - --hash=sha256:b3aa21bad31db904e0b9055e12c8282df62d43169c4a9d2929407060066ebc74 \ - --hash=sha256:b565f25171e04d4fad950d1fa837133e3af6ea6f509d96166eed745eb0cf63bc \ - --hash=sha256:b56993a7aeb4140c4770f4f7965c9e5af4f024457d06e23c01b0d47501cb18ed \ - --hash=sha256:b6acb765e7c1f2fa08ac9057a33595e26104d7d67046becae184a8f100932dd9 \ - --hash=sha256:b6f366a5ef66a2df4d9e68035cfe9f0eb8473cdfb922c37fac1d169b468607b0 \ - --hash=sha256:b7836aa13721dbdef658aebd11f60d00de633a95726521860fe1f6be75fa225a \ - --hash=sha256:b8fca73e16c49dd972ce3a88278dfa5b93bf91ddef332a46e9443abe21ca2f7c \ - --hash=sha256:b953d9d496d19786f4d46e6ba4b386c6e493e81e40f9c5392332458183b0599d \ - --hash=sha256:bbc458a292aee57d572075f22c035fa32969cdb7987d454e3e34d45a40a0a8b4 \ - --hash=sha256:c1cecea3e477af105f32ef2119b8d895f297492e41d317e60d474bc4bffd62ff \ - --hash=sha256:c1d7fa44aece1fa02b8927441614c96520253a5cad6a96994e3a81e060feed55 \ - --hash=sha256:c1ed17104d1be7f807fdec35ec99777168dd793a09510d753f8710590ba54cdd \ - --hash=sha256:c3c6f6b027d10f84bfe65049028892b5740878edd9eae5fea0d1710b09b1d257 \ - --hash=sha256:c5e0fdb5744caf1036dec5510f543164f2144cb64932251f6dfd42fa872b7f9c \ - --hash=sha256:c60f1de066eb5a0fd8ee5974de4194bb1c2e7692941458807162ffbc39887303 \ - --hash=sha256:c6d9c6e783b348f719b6118bb3f187b2e138e3112576c9679eb458cc8b2e164b \ - --hash=sha256:c940e00e8d3d10932c929d4b8657c2ea47d2560f31874c3e174c0d3488e8b865 \ - --hash=sha256:c9f261ad3cd97257dc1d9355bfbaa7dd703e06574bffa0fa8fe1e31da915ee38 \ - --hash=sha256:d21a07edddb3e0ca12a8b8712abc8452481c3d3db19ae87fc94e9842d005964b \ - --hash=sha256:d363660f9ef8c734495598d2f3e527fb41f745c73159dc0d743402f049fb6836 \ - --hash=sha256:d478a2ca902b6ef28ffc9521e5f0f728d036abe35c0b250ee8ae78cfe7c5e44e \ - --hash=sha256:d571f0b2eec3513734ea31a16ce0f7840c0b85a98e7edfa0e328ed144f9ef78f \ - --hash=sha256:d6b39a2cc5625bbc4fda18919a891eab9aab934eecf83660a90ce20c53621a9a \ - --hash=sha256:d76d62909bfb14521c3f7cfd5b94c0c75ec94b0a11f647d2f604998962ec7b6c \ - --hash=sha256:dab4178a0bc1ef13178832b12db7bc7f562e8f028b2b5be186e370090dc50652 \ - --hash=sha256:db976be51375bca900e008941639448d148c655c9545071965d0571ecc04f5d0 \ - --hash=sha256:ded4fc0edf3de792850cb8b04bbf3c5bd725eeaf9df4c27aad510f6eed9c4e19 \ - --hash=sha256:e006ea703d5c0f3d112b51ba18af73b58209b954acfe3d8da42eacc9a00e4be6 \ - --hash=sha256:e3e5d1802cba785210a4a800e63fcee7a228649a880f3bf7f2aadccb151a834b \ - --hash=sha256:e480d3dac06c89bc2e0fd87524cc38c546ac8b4a38177650745e64acbbcfdeba \ - --hash=sha256:e50af656c15e2723eeb7279c0837e07accc594b95ec18b86821a4d44b51b24bf \ - --hash=sha256:e83ce8008b48762be296f1401f19afd9ea29f3d035d1974e0cecb74e9afbd1df \ - --hash=sha256:ed3b8281c5d0944d939c82db4ec2300409dd69ee087f7a75a94f2e301e855fb4 \ - --hash=sha256:ef250a3f5e93182193f5c927c5e9575b2cb14b80d03e258bc0b89cc5de076b60 \ - --hash=sha256:f1574566457161678297a116fa5d1556c5a4159d64c5ff7c760e7c564bf66f16 \ - --hash=sha256:f26262900edd16272b6360014495e8d68379c6c6e95983f9b7b322dc928a1194 \ - --hash=sha256:f28eac18a8733a124444643a66ac96fef2c0ad65f50034e0a043b90333dc677f \ - --hash=sha256:f54840bea73541652f1170dc63402a5b776fc851ad36a842da9e5163c1f504a0 \ - --hash=sha256:f785f44a44702dea89b28bce5bc82552490694ce4e144e21a4f0545e364d2150 \ - --hash=sha256:f7cc00089b4c21847852c0ad76fb3680f9833b855a0d30bcec94211c435bff6b \ - --hash=sha256:f95bd07f301135771559101c060f558e2cf896c7df00bec050ca7f93bf11585a \ - --hash=sha256:fc8ced733d6cd9af5e412f256a32f7c61cd2d7371280a65c689939ac4572499f \ - --hash=sha256:fd03e38068faeef937cc6761a250a4aaa015564bd0d61481fefcf15586d31825 +regex==2026.4.4 \ + --hash=sha256:011bb48bffc1b46553ac704c975b3348717f4e4aa7a67522b51906f99da1820c \ + --hash=sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f \ + --hash=sha256:0540e5b733618a2f84e9cb3e812c8afa82e151ca8e19cf6c4e95c5a65198236f \ + --hash=sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62 \ + --hash=sha256:0709f22a56798457ae317bcce42aacee33c680068a8f14097430d9f9ba364bee \ + --hash=sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883 \ + --hash=sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13 \ + --hash=sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99 \ + --hash=sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a \ + --hash=sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0 \ + --hash=sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566 \ + --hash=sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9 \ + --hash=sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76 \ + --hash=sha256:1b9a00b83f3a40e09859c78920571dcb83293c8004079653dd22ec14bbfa98c7 \ + --hash=sha256:21e5eb86179b4c67b5759d452ea7c48eb135cd93308e7a260aa489ed2eb423a4 \ + --hash=sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717 \ + --hash=sha256:2895506ebe32cc63eeed8f80e6eae453171cfccccab35b70dc3129abec35a5b8 \ + --hash=sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17 \ + --hash=sha256:2a5d273181b560ef8397c8825f2b9d57013de744da9e8257b8467e5da8599351 \ + --hash=sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d \ + --hash=sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb \ + --hash=sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7 \ + --hash=sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8 \ + --hash=sha256:312ec9dd1ae7d96abd8c5a36a552b2139931914407d26fba723f9e53c8186f86 \ + --hash=sha256:33424f5188a7db12958246a54f59a435b6cb62c5cf9c8d71f7cc49475a5fdada \ + --hash=sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81 \ + --hash=sha256:33bfda9684646d323414df7abe5692c61d297dbb0530b28ec66442e768813c59 \ + --hash=sha256:349d7310eddff40429a099c08d995c6d4a4bfaf3ff40bd3b5e5cb5a5a3c7d453 \ + --hash=sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141 \ + --hash=sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031 \ + --hash=sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74 \ + --hash=sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244 \ + --hash=sha256:415a994b536440f5011aa77e50a4274d15da3245e876e5c7f19da349caaedd87 \ + --hash=sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f \ + --hash=sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465 \ + --hash=sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983 \ + --hash=sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff \ + --hash=sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0 \ + --hash=sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55 \ + --hash=sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752 \ + --hash=sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73 \ + --hash=sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe \ + --hash=sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95 \ + --hash=sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8 \ + --hash=sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb \ + --hash=sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45 \ + --hash=sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943 \ + --hash=sha256:6780f008ee81381c737634e75c24e5a6569cc883c4f8e37a37917ee79efcafd9 \ + --hash=sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520 \ + --hash=sha256:6aa809ed4dc3706cc38594d67e641601bd2f36d5555b2780ff074edfcb136cf8 \ + --hash=sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1 \ + --hash=sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3 \ + --hash=sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1 \ + --hash=sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb \ + --hash=sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6 \ + --hash=sha256:74fa82dcc8143386c7c0392e18032009d1db715c25f4ba22d23dc2e04d02a20f \ + --hash=sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be \ + --hash=sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4 \ + --hash=sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951 \ + --hash=sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27 \ + --hash=sha256:7d346fccdde28abba117cc9edc696b9518c3307fbfcb689e549d9b5979018c6d \ + --hash=sha256:8512fcdb43f1bf18582698a478b5ab73f9c1667a5b7548761329ef410cd0a760 \ + --hash=sha256:867bddc63109a0276f5a31999e4c8e0eb7bbbad7d6166e28d969a2c1afeb97f9 \ + --hash=sha256:88e9b048345c613f253bea4645b2fe7e579782b82cac99b1daad81e29cc2ed8e \ + --hash=sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7 \ + --hash=sha256:9542ccc1e689e752594309444081582f7be2fdb2df75acafea8a075108566735 \ + --hash=sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81 \ + --hash=sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3 \ + --hash=sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9 \ + --hash=sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790 \ + --hash=sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043 \ + --hash=sha256:a0d2b28aa1354c7cd7f71b7658c4326f7facac106edd7f40eda984424229fd59 \ + --hash=sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a \ + --hash=sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4 \ + --hash=sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f \ + --hash=sha256:a85b620a388d6c9caa12189233109e236b3da3deffe4ff11b84ae84e218a274f \ + --hash=sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427 \ + --hash=sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae \ + --hash=sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa \ + --hash=sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d \ + --hash=sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0 \ + --hash=sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc \ + --hash=sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863 \ + --hash=sha256:b4c36a85b00fadb85db9d9e90144af0a980e1a3d2ef9cd0f8a5bef88054657c6 \ + --hash=sha256:b5f9fb784824a042be3455b53d0b112655686fdb7a91f88f095f3fee1e2a2a54 \ + --hash=sha256:be061028481186ba62a0f4c5f1cc1e3d5ab8bce70c89236ebe01023883bc903b \ + --hash=sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52 \ + --hash=sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07 \ + --hash=sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b \ + --hash=sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b \ + --hash=sha256:cf9b1b2e692d4877880388934ac746c99552ce6bf40792a767fd42c8c99f136d \ + --hash=sha256:d2228c02b368d69b724c36e96d3d1da721561fb9cc7faa373d7bf65e07d75cb5 \ + --hash=sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf \ + --hash=sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b \ + --hash=sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359 \ + --hash=sha256:dcb5453ecf9cd58b562967badd1edbf092b0588a3af9e32ee3d05c985077ce87 \ + --hash=sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca \ + --hash=sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa \ + --hash=sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423 \ + --hash=sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4 \ + --hash=sha256:e355be718caf838aa089870259cf1776dc2a4aa980514af9d02c59544d9a8b22 \ + --hash=sha256:e7ab63e9fe45a9ec3417509e18116b367e89c9ceb6219222a3396fa30b147f80 \ + --hash=sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f \ + --hash=sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17 \ + --hash=sha256:eb59c65069498dbae3c0ef07bbe224e1eaa079825a437fb47a479f0af11f774f \ + --hash=sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e \ + --hash=sha256:ee9627de8587c1a22201cb16d0296ab92b4df5cdcb5349f4e9744d61db7c7c98 \ + --hash=sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4 \ + --hash=sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d \ + --hash=sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b \ + --hash=sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c \ + --hash=sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83 \ + --hash=sha256:fe896e07a5a2462308297e515c0054e9ec2dd18dfdc9427b19900b37dfe6f40b \ + --hash=sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e # via # feast (pyproject.toml) # parsimonious @@ -4772,9 +4853,9 @@ rfc3987-syntax==1.1.0 \ --hash=sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f \ --hash=sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d # via jsonschema -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 # via # fastapi-mcp # ibis-framework @@ -4915,25 +4996,25 @@ ruamel-yaml==0.17.17 \ --hash=sha256:9751de4cbb57d4bfbf8fc394e125ed4a2f170fbff3dc3d78abf50be85924f8be \ --hash=sha256:9af3ec5d7f8065582f3aa841305465025d0afd26c5fb54e15b964e11838fc74f # via great-expectations -ruff==0.15.9 \ - --hash=sha256:058d8e99e1bfe79d8a0def0b481c56059ee6716214f7e425d8e737e412d69677 \ - --hash=sha256:0694e601c028fd97dc5c6ee244675bc241aeefced7ef80cd9c6935a871078f53 \ - --hash=sha256:29cbb1255a9797903f6dde5ba0188c707907ff44a9006eb273b5a17bfa0739a2 \ - --hash=sha256:2b0c7c341f68adb01c488c3b7d4b49aa8ea97409eae6462d860a79cf55f431b6 \ - --hash=sha256:45a70921b80e1c10cf0b734ef09421f71b5aa11d27404edc89d7e8a69505e43d \ - --hash=sha256:4965bac6ac9ea86772f4e23587746f0b7a395eccabb823eb8bfacc3fa06069f7 \ - --hash=sha256:55cc15eee27dc0eebdfcb0d185a6153420efbedc15eb1d38fe5e685657b0f840 \ - --hash=sha256:6d3fcbca7388b066139c523bda744c822258ebdcfbba7d24410c3f454cc9af71 \ - --hash=sha256:6efbe303983441c51975c243e26dff328aca11f94b70992f35b093c2e71801e1 \ - --hash=sha256:7b34a9766aeec27a222373d0b055722900fbc0582b24f39661aa96f3fe6ad901 \ - --hash=sha256:89dd695bc72ae76ff484ae54b7e8b0f6b50f49046e198355e44ea656e521fef9 \ - --hash=sha256:8e1ddb11dbd61d5983fa2d7d6370ef3eb210951e443cace19594c01c72abab4c \ - --hash=sha256:9439a342adb8725f32f92732e2bafb6d5246bd7a5021101166b223d312e8fc59 \ - --hash=sha256:9c5e6faf9d97c8edc43877c3f406f47446fc48c40e1442d58cfcdaba2acea745 \ - --hash=sha256:a6537f6eed5cda688c81073d46ffdfb962a5f29ecb6f7e770b2dc920598997ed \ - --hash=sha256:bde6ff36eaf72b700f32b7196088970bf8fdb2b917b7accd8c371bfc0fd573ec \ - --hash=sha256:ce187224ef1de1bd225bc9a152ac7102a6171107f026e81f317e4257052916d5 \ - --hash=sha256:eaf05aad70ca5b5a0a4b0e080df3a6b699803916d88f006efd1f5b46302daab8 +ruff==0.15.12 \ + --hash=sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b \ + --hash=sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33 \ + --hash=sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0 \ + --hash=sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002 \ + --hash=sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339 \ + --hash=sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e \ + --hash=sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847 \ + --hash=sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f \ + --hash=sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6 \ + --hash=sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d \ + --hash=sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20 \ + --hash=sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd \ + --hash=sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c \ + --hash=sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5 \ + --hash=sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6 \ + --hash=sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c \ + --hash=sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5 \ + --hash=sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5 # via feast (pyproject.toml) s3transfer==0.13.1 \ --hash=sha256:a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724 \ @@ -5089,9 +5170,9 @@ send2trash==2.1.0 \ --hash=sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c \ --hash=sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459 # via jupyter-server -sentence-transformers==5.3.0 \ - --hash=sha256:414a0a881f53a4df0e6cbace75f823bfcb6b94d674c42a384b498959b7c065e2 \ - --hash=sha256:dca6b98db790274a68185d27a65801b58b4caf653a4e556b5f62827509347c7d +sentence-transformers==5.4.1 \ + --hash=sha256:436bcb1182a0ff42a8fb2b1c43498a70d0a75b688d182f2cd0d1dd115af61ddc \ + --hash=sha256:a6d640fc363849b63affb8e140e9d328feabab86f83d58ac3e16b1c28140b790 # via feast (pyproject.toml) setuptools==80.10.2 \ --hash=sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70 \ @@ -5263,103 +5344,103 @@ sphinxcontrib-serializinghtml==2.0.0 \ --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \ --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d # via sphinx -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot[rs]==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot[rs]==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via # feast (pyproject.toml) # ibis-framework -sqlglotc==30.2.1 \ - --hash=sha256:052cd7bb41fc9b841eb268d4dd601eb6b5954b7c6d5656795d4350a0f8020d53 \ - --hash=sha256:058f0e9aed2b8dff87dc893b8793e514204c8dfef699b7d3d1704dfbdd949f2b \ - --hash=sha256:0e6be524252894c0fa98d25d4e60dfae6485ba66ca1abd40bf05f16a9cf26baf \ - --hash=sha256:13f8f68808777ba7d845bc908bf09f72a0c9899a19811483dc52f0fa48b38d5a \ - --hash=sha256:1a004086ab871be0cc97766f7b6fb8866729f09dd7272254fd31c05107f3fdc8 \ - --hash=sha256:25c6f62f31cd3a051285635c3f6a01d2f3c73ca2baaa26970815166928042ace \ - --hash=sha256:2b5fe8adc1a1e2fb819e014e94974a274f30dbf9684ceed9f171fb0889f80f0b \ - --hash=sha256:2ffe527bc8664b03cc936bae7ebf965f482beb4acee7a815c2ec2d9aea720b4e \ - --hash=sha256:4aa90e08f53409b1857572836e57a31835ed20e32521c6fafdc6af96199baff7 \ - --hash=sha256:507935a971e0a9e5d4ac7ca14df479f8e270502b44904f71d95c0aaed066006f \ - --hash=sha256:515e092ab8fb522b256fa8a34f471e9b187bb8a50a7c0226a65b036a07d6d188 \ - --hash=sha256:585bb610fde3e3dd1d7e5ff3cce14f70fbd53ced6769cd104679adf8b5c4ab5b \ - --hash=sha256:850e7517dd4739cad9af65bcb9699825f9202e5971407bf955e3248fe4814f96 \ - --hash=sha256:8f063af733cbcc51686380470e7f3f80b589b8c58084baa138efb3b8ca821597 \ - --hash=sha256:b17e3002ed10747388367621b2ecf39c06d5fdc6b3c31a8c32be2f5ef546fc0b \ - --hash=sha256:d577e1635e127febb7012bc42fa1c3b958076e59a1a116ade20048c572a1be42 \ - --hash=sha256:dc292cd73e0c447253877c27f00454a2d09b71324a130ad4c58c145ab753889e \ - --hash=sha256:de168df756a21a028cf1f917f92da2f77bb135f3b6cdd960914460942a5eca10 \ - --hash=sha256:de884dd224220002c3e940ca5bdceb27ef9638e5f02493db133ffb8ae88b5610 \ - --hash=sha256:f33c7d1646ff6531cb9b07f0740b2939f3ecaa31efebfbec8adb6b275f1a45f2 \ - --hash=sha256:f9a1fc7b1ff3b51d0d03a391768a79964f68541b4c2f294a25a6f14e6670ffab \ - --hash=sha256:fae4edad0b7c5f9f963bd63452f722f0d7f77a436c2d334b555b31722f9573ad \ - --hash=sha256:fdc19623a1c7659918c3cee18ea8849fc4af9eaeb87247acf37e0393295d32b7 \ - --hash=sha256:feefc0ab7606d1fe284d23bef09ea4829ce4fad679936959c29324310f23e081 \ - --hash=sha256:ff19b7ecb931aef6c7c6168af5530c07e67915102b701d45ae80446f0695ba54 +sqlglotc==30.6.0 \ + --hash=sha256:003b15bbe3f3a4a63313baeb3f090906cf0172fe007ab24974c612a577f56c61 \ + --hash=sha256:05861b5d74ae4b0a5a6e6a9309a8a975d9572c2bf1eb9634a33af9189eb7d333 \ + --hash=sha256:0b907206ef36fd8f0c28da4c5b8c8f896bd67826da0d765616e6c950689ac849 \ + --hash=sha256:0e1aae195008c1d87ed1b1543bceed1408ae8ecf146571b3029e98b23a19c59c \ + --hash=sha256:16291ee3da0276df2689cc139df41d8723cf6014aca979f307fb75a898b0d3e8 \ + --hash=sha256:27d6c22375395f1fdfe8a5d80b5fb781ca7849e29c7db7dfb11edb466a5b7a4d \ + --hash=sha256:2b50aba396d4622c201a9dd933b51cd6858b5af5ebbcf7db1af35db7e83ece48 \ + --hash=sha256:4a5beba24625bc14070992fdddc7aed22df912007882f31f27b0e88d7b7d9445 \ + --hash=sha256:4c84a933816374b6167d9347e488fb4a357bb0e3be2e8e820dafeb3c9948feab \ + --hash=sha256:4d45ee83e1f72ee94045ccfd13e51fa7d822548f50e9d20d3d42e127bcd9f453 \ + --hash=sha256:4eb6f349c21d5a3e39733db5416e57ec171ca3b1c17b02badfdbe55d9a3666a5 \ + --hash=sha256:61a1e4f533955db0bfd4219883bfab1f2030753ffaebc9cbb4e950dfbcae3db7 \ + --hash=sha256:63b02231cc2f10d63df373fa02f3e03b4216e94c6f32ccdfd11adc770dc95fb7 \ + --hash=sha256:6e8dab3b9f84c9b591ecd1fb0920b800c08dd6c38eda3be91176fc239d3c94bc \ + --hash=sha256:7acdb7b15f060bacea01e1c68d1dd70471f03bda89478ca0ee96bac3df03d0a7 \ + --hash=sha256:87437d4f32c1dc61c3ef046dc39b9fe119ac2e2e5253e33351f1a62ba802942a \ + --hash=sha256:875c6925f3d70aa1bc4bcc405090a12deec01de71193e7229f114c5ecf39f725 \ + --hash=sha256:949f5d457c8c98998ed06d060873370d7553077ab3c2db9569acda5887cd6ce7 \ + --hash=sha256:c69c9dbe4dfab74329294d07b2956ca9a03123089a1923f3d299a21d4b66898d \ + --hash=sha256:cb4b5a532fb35ce415aea6d360fe202278b46cc59c07318b97401930a1584e35 \ + --hash=sha256:cfba6244f52bcdffe22a35334d48d741aed3077c20bb7aa9bb511c21fd766438 \ + --hash=sha256:dd781790c3fc6cb82e6c00836f7f9ab33941cf2b9dca173d7b9f41c37c78d114 \ + --hash=sha256:e397befde3d08d870a8f1c7bd80d7abc68ee119c472143ab196709319e205af4 \ + --hash=sha256:f0a37ad865b106005cb5d36efd537a412912477c83cbca1579ee45f17d73fe54 \ + --hash=sha256:fb977d427a196a620aa60b71d4b66d60c3d015c9004e7393162d4771fb435406 # via sqlglot sqlglotrs==0.13.0 \ --hash=sha256:6b934a244b16f26fca50974328a2ebc7689583c59f06203cebb46e2e6e8d93a7 \ @@ -5766,15 +5847,16 @@ tree-sitter==0.25.2 \ --hash=sha256:fbb1706407c0e451c4f8cc016fec27d72d4b211fdd3173320b1ada7a6c74c3ac \ --hash=sha256:fe43c158555da46723b28b52e058ad444195afd1db3ca7720c59a254544e9c20 # via docling-core -tree-sitter-c==0.24.1 \ - --hash=sha256:290bff0f9c79c966496ebae45042f77543e6e4aea725f40587a8611d566231a8 \ - --hash=sha256:789781afcb710df34144f7e2a20cd80e325114b9119e3956c6bd1dd2d365df98 \ - --hash=sha256:7d2d0cda0b8dda428c81440c1e94367f9f13548eedca3f49768bde66b1422ad6 \ - --hash=sha256:942bcd7cbecd810dcf7ca6f8f834391ebf0771a89479646d891ba4ca2fdfdc88 \ - --hash=sha256:9a74cfd7a11ca5a961fafd4d751892ee65acae667d2818968a6f079397d8d28c \ - --hash=sha256:9c06ac26a1efdcc8b26a8a6970fbc6997c4071857359e5837d4c42892d45fe1e \ - --hash=sha256:a6a807705a3978911dc7ee26a7ad36dcfacb6adfc13c190d496660ec9bd66707 \ - --hash=sha256:d46bbda06f838c2dcb91daf767813671fd366b49ad84ff37db702129267b46e1 +tree-sitter-c==0.24.2 \ + --hash=sha256:1628584df0299b5a340aa63f8e67b6c97c91517f52fa7e7a4c557e40adb330a9 \ + --hash=sha256:4a2f4371cd816cc3153458f69062135ebb2ea5f275ddd90494e5c823d778204a \ + --hash=sha256:4d4579a8b54f0a442f903d88d3304cab77cd5c2031d4015baa4f2f8e15d6dcb7 \ + --hash=sha256:5041ef67eb68ce6bc8bb0b1f8ef3a5585ce523dae0c7eec109ab0627dd75aede \ + --hash=sha256:82842c5a5f2acd93f4de10038c33ac179c8979defc39376f990348d6289e933b \ + --hash=sha256:97bc80a224d48215d4e6e6376bf30d114f4c317b8145ff1b02afe785d4ba7bdd \ + --hash=sha256:abb549225091f7b25df2dd3a0143ece6e208f7055d8bcb4700b41ee79b9ef1e1 \ + --hash=sha256:c098bedcd5ac86ff93fa734d51d1dd86aed40fd5ed7d634c7af11380a0469969 \ + --hash=sha256:e2b42e8e22202c251f8629306f9321233542e07a6e01611b5fe83489272143eb # via docling-core tree-sitter-javascript==0.25.0 \ --hash=sha256:199d09985190852e0912da2b8d26c932159be314bc04952cf917ed0e4c633e6b \ @@ -5823,9 +5905,9 @@ typer==0.12.5 \ # docling # docling-core # fastapi-mcp -types-cffi==2.0.0.20260402 \ - --hash=sha256:47e1320c009f630c59c55c8e3d2b8c501e280babf52e92f6109cbfb0864ba367 \ - --hash=sha256:f647a400fba0a31d603479169d82ee5359db79bd1136e41dc7e6489296e3a2b2 +types-cffi==2.0.0.20260408 \ + --hash=sha256:68bd296742b4ff7c0afe3547f50bd0acc55416ecf322ffefd2b7344ef6388a42 \ + --hash=sha256:aa8b9c456ab715c079fc655929811f21f331bfb940f4a821987c581bf4e36230 # via types-pyopenssl types-protobuf==3.19.22 \ --hash=sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab \ @@ -5833,25 +5915,25 @@ types-protobuf==3.19.22 \ # via # feast (pyproject.toml) # mypy-protobuf -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) types-pyopenssl==24.1.0.20240722 \ --hash=sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39 \ --hash=sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54 # via types-redis -types-python-dateutil==2.9.0.20260402 \ - --hash=sha256:7827e6a9c93587cc18e766944254d1351a2396262e4abe1510cbbd7601c5e01f \ - --hash=sha256:a980142b9966713acb382c467e35c5cc4208a2f91b10b8d785a0ae6765df6c0b +types-python-dateutil==2.9.0.20260408 \ + --hash=sha256:473139d514a71c9d1fbd8bb328974bedcb1cc3dba57aad04ffa4157f483c216f \ + --hash=sha256:8b056ec01568674235f64ecbcef928972a5fac412f5aab09c516dfa2acfbb582 # via feast (pyproject.toml) -types-pytz==2026.1.1.20260402 \ - --hash=sha256:0d9a60ed1c6ad4fce7c6395b5bd2d9827db41d4b83de7c0322cf85869c2bfda3 \ - --hash=sha256:79209aa51dc003a4a6a764234d92b14e5c09a1b7f24e0f00c493929fd33618e8 +types-pytz==2026.1.1.20260408 \ + --hash=sha256:89b6a34b9198ea2a4b98a9d15cbca987053f52a105fd44f7ce3789cae4349408 \ + --hash=sha256:c7e4dec76221fb7d0c97b91ad8561d689bebe39b6bcb7b728387e7ffd8cde788 # via feast (pyproject.toml) -types-pyyaml==6.0.12.20250915 \ - --hash=sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3 \ - --hash=sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6 +types-pyyaml==6.0.12.20260408 \ + --hash=sha256:92a73f2b8d7f39ef392a38131f76b970f8c66e4c42b3125ae872b7c93b556307 \ + --hash=sha256:fbc42037d12159d9c801ebfcc79ebd28335a7c13b08a4cfbc6916df78fee9384 # via feast (pyproject.toml) types-redis==4.6.0.20241004 \ --hash=sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e \ @@ -5861,15 +5943,15 @@ types-requests==2.30.0.0 \ --hash=sha256:c6cf08e120ca9f0dc4fa4e32c3f953c3fba222bcc1db6b97695bce8da1ba9864 \ --hash=sha256:dec781054324a70ba64430ae9e62e7e9c8e4618c185a5cb3f87a6738251b5a31 # via feast (pyproject.toml) -types-setuptools==82.0.0.20260402 \ - --hash=sha256:4b9a9f6c3c4c65107a3956ad6a6acbccec38e398ff6d5f78d5df7f103dadb8d6 \ - --hash=sha256:63d2b10ba7958396ad79bbc24d2f6311484e452daad4637ffd40407983a27069 +types-setuptools==82.0.0.20260408 \ + --hash=sha256:036c68caf7e672a699f5ebbf914708d40644c14e05298bc49f7272be91cf43d3 \ + --hash=sha256:ece0a215cdfa6463a65fd6f68bd940f39e455729300ddfe61cab1147ed1d2462 # via # feast (pyproject.toml) # types-cffi -types-tabulate==0.10.0.20260308 \ - --hash=sha256:724dcb1330ffba5f46d3cf6e29f45089fccb8e85801e6e7ac9efb1195bf7bea1 \ - --hash=sha256:94a9795965bc6290f844d61e8680a1270040664b88fd12014624090fd847e13c +types-tabulate==0.10.0.20260408 \ + --hash=sha256:2b19d193603d38c34645de53c0c1087e2364487d518d4a2f44268db2366723cc \ + --hash=sha256:903d62fdf7e5a0ff659fd5d629df716232f7658c6d30e98f0374488d06ffacf4 # via feast (pyproject.toml) types-urllib3==1.26.25.14 \ --hash=sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f \ @@ -5930,9 +6012,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # arrow # ibis-framework @@ -6310,9 +6392,9 @@ werkzeug==3.1.8 \ --hash=sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50 \ --hash=sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44 # via moto -wheel==0.46.3 \ - --hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d \ - --hash=sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803 +wheel==0.47.0 \ + --hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \ + --hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3 # via # pip-tools # singlestoredb @@ -6404,6 +6486,7 @@ wrapt==1.17.3 \ --hash=sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c # via # aiobotocore + # deprecated # testcontainers xlsxwriter==3.2.9 \ --hash=sha256:254b1c37a368c444eac6e2f867405cc9e461b0ed97a3233b2ac1e574efb4140c \ @@ -6685,9 +6768,9 @@ yarl==1.23.0 \ --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \ --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d # via aiohttp -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata zstandard==0.25.0 \ --hash=sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64 \ diff --git a/sdk/python/requirements/py3.10-minimal-requirements.txt b/sdk/python/requirements/py3.10-minimal-requirements.txt index aafdc1baddc..35b7cce797a 100644 --- a/sdk/python/requirements/py3.10-minimal-requirements.txt +++ b/sdk/python/requirements/py3.10-minimal-requirements.txt @@ -196,9 +196,9 @@ botocore==1.38.46 \ # boto3 # s3transfer # snowflake-connector-python -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # httpcore # httpx @@ -426,9 +426,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -442,56 +442,56 @@ colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via feast (pyproject.toml) -cryptography==46.0.6 \ - --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ - --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ - --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ - --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ - --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ - --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ - --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ - --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ - --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ - --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ - --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ - --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ - --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ - --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ - --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ - --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ - --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ - --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ - --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ - --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ - --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ - --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ - --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ - --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ - --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ - --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ - --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ - --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ - --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ - --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ - --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ - --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ - --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ - --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ - --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ - --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ - --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ - --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ - --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ - --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 +cryptography==47.0.0 \ + --hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \ + --hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \ + --hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \ + --hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \ + --hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \ + --hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \ + --hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \ + --hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \ + --hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \ + --hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \ + --hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \ + --hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \ + --hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \ + --hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \ + --hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \ + --hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \ + --hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \ + --hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \ + --hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \ + --hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \ + --hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \ + --hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \ + --hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \ + --hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \ + --hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \ + --hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \ + --hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \ + --hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \ + --hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \ + --hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \ + --hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \ + --hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \ + --hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \ + --hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \ + --hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \ + --hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \ + --hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \ + --hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \ + --hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \ + --hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \ + --hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \ + --hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \ + --hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \ + --hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \ + --hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \ + --hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \ + --hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \ + --hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \ + --hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736 # via # google-auth # pyjwt @@ -511,42 +511,42 @@ dill==0.3.9 \ --hash=sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a \ --hash=sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c # via feast (pyproject.toml) -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ @@ -556,9 +556,9 @@ exceptiongroup==1.3.1 \ --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 # via anyio -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -566,9 +566,9 @@ fastapi-mcp==0.4.0 \ --hash=sha256:d4a3fe7966af24d44e4b412720561c95eb12bed999a4443a88221834b3b15aec \ --hash=sha256:d4ca9410996f4c7b8ea0d7b20fdf79878dc359ebf89cbf3b222e0b675a55097d # via feast (pyproject.toml) -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via snowflake-connector-python frozenlist==1.8.0 \ --hash=sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686 \ @@ -710,9 +710,9 @@ fsspec==2024.9.0 \ # via # feast (pyproject.toml) # dask -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -722,9 +722,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-datastore # google-cloud-storage # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -1078,9 +1078,9 @@ ibis-framework[duckdb]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1116,97 +1116,97 @@ kubernetes==35.0.0 \ --hash=sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d \ --hash=sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee # via feast (pyproject.toml) -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -1583,51 +1583,51 @@ multidict==6.7.1 \ # aiobotocore # aiohttp # yarl -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -1700,9 +1700,9 @@ oauthlib==3.3.1 \ --hash=sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9 \ --hash=sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1 # via requests-oauthlib -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # db-dtypes @@ -1776,9 +1776,9 @@ pandas==2.3.3 \ # pandas-gbq # pymilvus # snowflake-connector-python -pandas-gbq==0.34.1 \ - --hash=sha256:6bea5b85937251b976cf9db38151ea59abbff98771179183488d4614694bff67 \ - --hash=sha256:b74932c6ee35dfc81582f39c792e3a68c9ef9bee8c85f25667d9d05dfadd0daf +pandas-gbq==0.35.0 \ + --hash=sha256:258de481019566611031919997bf9c1ece4ca30a4dd02d3fc3664b251d446182 \ + --hash=sha256:596c908487ef0649a161e86ef272c00c267e581b95dc5ee0dc3518545b33bcfc # via google-cloud-bigquery parsy==2.2 \ --hash=sha256:5e981613d9d2d8b68012d1dd0afe928967bea2e4eefdb76c2f545af0dd02a9e7 \ @@ -1788,17 +1788,17 @@ partd==1.4.2 \ --hash=sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f \ --hash=sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c # via dask -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via mypy -platformdirs==4.9.4 \ - --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ - --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 +platformdirs==4.9.6 \ + --hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \ + --hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917 # via snowflake-connector-python -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) propcache==0.4.1 \ --hash=sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e \ @@ -1994,57 +1994,57 @@ psycopg-pool==3.3.0 \ --hash=sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 \ --hash=sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5 # via psycopg -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -2069,141 +2069,140 @@ pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi # fastapi-mcp # mcp # pydantic-settings -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # fastapi-mcp # mcp @@ -2232,9 +2231,9 @@ pymysql==1.1.2 \ --hash=sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03 \ --hash=sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9 # via feast (pyproject.toml) -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python python-dateutil==2.9.0.post0 \ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ @@ -2253,9 +2252,9 @@ python-dotenv==1.2.2 \ # pydantic-settings # pymilvus # uvicorn -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp pytz==2026.1.post1 \ --hash=sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1 \ @@ -2370,9 +2369,9 @@ requests-oauthlib==2.0.0 \ # via # google-auth-oauthlib # kubernetes -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 # via # fastapi-mcp # ibis-framework @@ -2549,74 +2548,74 @@ sortedcontainers==2.4.0 \ --hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \ --hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0 # via snowflake-connector-python -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via ibis-framework sse-starlette==3.3.4 \ --hash=sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1 \ @@ -2713,13 +2712,13 @@ typeguard==4.5.1 \ --hash=sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40 \ --hash=sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274 # via feast (pyproject.toml) -typer==0.24.1 \ - --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e \ - --hash=sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45 +typer==0.24.2 \ + --hash=sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8 \ + --hash=sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480 # via fastapi-mcp -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -2755,9 +2754,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # ibis-framework # pandas @@ -3302,7 +3301,7 @@ yarl==1.23.0 \ --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \ --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d # via aiohttp -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata diff --git a/sdk/python/requirements/py3.10-minimal-sdist-requirements-build.txt b/sdk/python/requirements/py3.10-minimal-sdist-requirements-build.txt index 3ecce8ab7be..4f20701dc65 100644 --- a/sdk/python/requirements/py3.10-minimal-sdist-requirements-build.txt +++ b/sdk/python/requirements/py3.10-minimal-sdist-requirements-build.txt @@ -214,9 +214,9 @@ cython==3.2.4 \ # via # pyarrow # uvloop -dunamai==1.26.0 \ - --hash=sha256:5396ac43aa20ed059040034e9f9798c7464cf4334c6fc3da3732e29273a2f97d \ - --hash=sha256:f584edf0fda0d308cce0961f807bc90a8fe3d9ff4d62f94e72eca7b43f0ed5f6 +dunamai==1.26.1 \ + --hash=sha256:2727d939c5b4257cb01ea404372803b477f5176e5a347c43beaf89cd5072e853 \ + --hash=sha256:3b46007bd65b00b4824ead0a1aee365fd22d0ec2b9c219497d4fd48f52860c8b # via uv-dynamic-versioning exceptiongroup==1.3.1 \ --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ @@ -265,9 +265,9 @@ gitdb==4.0.12 \ --hash=sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571 \ --hash=sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf # via gitpython -gitpython==3.1.46 \ - --hash=sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f \ - --hash=sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058 +gitpython==3.1.47 \ + --hash=sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905 \ + --hash=sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd # via pymilvus hatch-fancy-pypi-readme==25.1.0 \ --hash=sha256:9c58ed3dff90d51f43414ce37009ad1d5b0f08ffc9fc216998a06380f01c0045 \ @@ -334,97 +334,164 @@ jinja2==3.1.6 \ --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 # via uv-dynamic-versioning -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +libcst==1.8.6 \ + --hash=sha256:04030ea4d39d69a65873b1d4d877def1c3951a7ada1824242539e399b8763d30 \ + --hash=sha256:06fc56335a45d61b7c1b856bfab4587b84cfe31e9d6368f60bb3c9129d900f58 \ + --hash=sha256:089c58e75cb142ec33738a1a4ea7760a28b40c078ab2fd26b270dac7d2633a4d \ + --hash=sha256:08bd63a8ce674be431260649e70fca1d43f1554f1591eac657f403ff8ef82c7a \ + --hash=sha256:0c13d5bd3d8414a129e9dccaf0e5785108a4441e9b266e1e5e9d1f82d1b943c9 \ + --hash=sha256:0cbe17067055829607c5ba4afa46bfa4d0dd554c0b5a583546e690b7367a29b6 \ + --hash=sha256:16cfe0cfca5fd840e1fb2c30afb628b023d3085b30c3484a79b61eae9d6fe7ba \ + --hash=sha256:1a3a5e4ee870907aa85a4076c914ae69066715a2741b821d9bf16f9579de1105 \ + --hash=sha256:1dc3b897c8b0f7323412da3f4ad12b16b909150efc42238e19cbf19b561cc330 \ + --hash=sha256:203ec2a83f259baf686b9526268cd23d048d38be5589594ef143aee50a4faf7e \ + --hash=sha256:207481197afd328aa91d02670c15b48d0256e676ce1ad4bafb6dc2b593cc58f1 \ + --hash=sha256:25eaeae6567091443b5374b4c7d33a33636a2d58f5eda02135e96fc6c8807786 \ + --hash=sha256:25fc7a1303cad7639ad45ec38c06789b4540b7258e9a108924aaa2c132af4aca \ + --hash=sha256:2f04d3672bde1704f383a19e8f8331521abdbc1ed13abb349325a02ac56e5012 \ + --hash=sha256:351ab879c2fd20d9cb2844ed1ea3e617ed72854d3d1e2b0880ede9c3eea43ba8 \ + --hash=sha256:36473e47cb199b7e6531d653ee6ffed057de1d179301e6c67f651f3af0b499d6 \ + --hash=sha256:3649a813660fbffd7bc24d3f810b1f75ac98bd40d9d6f56d1f0ee38579021073 \ + --hash=sha256:375965f34cc6f09f5f809244d3ff9bd4f6cb6699f571121cebce53622e7e0b86 \ + --hash=sha256:3a926a4b42015ee24ddfc8ae940c97bd99483d286b315b3ce82f3bafd9f53474 \ + --hash=sha256:3f4fbb7f569e69fd9e89d9d9caa57ca42c577c28ed05062f96a8c207594e75b8 \ + --hash=sha256:42a4f68121e2e9c29f49c97f6154e8527cd31021809cc4a941c7270aa64f41aa \ + --hash=sha256:44f38139fa95e488db0f8976f9c7ca39a64d6bc09f2eceef260aa1f6da6a2e42 \ + --hash=sha256:455f49a93aea4070132c30ebb6c07c2dea0ba6c1fde5ffde59fc45dbb9cfbe4b \ + --hash=sha256:4d7bbdd35f3abdfb5ac5d1a674923572dab892b126a58da81ff2726102d6ec2e \ + --hash=sha256:4fc3fef8a2c983e7abf5d633e1884c5dd6fa0dcb8f6e32035abd3d3803a3a196 \ + --hash=sha256:536567441182a62fb706e7aa954aca034827b19746832205953b2c725d254a93 \ + --hash=sha256:5432e785322aba3170352f6e72b32bea58d28abd141ac37cc9b0bf6b7c778f58 \ + --hash=sha256:55ec021a296960c92e5a33b8d93e8ad4182b0eab657021f45262510a58223de1 \ + --hash=sha256:59a7e388c57d21d63722018978a8ddba7b176e3a99bd34b9b84a576ed53f2978 \ + --hash=sha256:5dcaaebc835dfe5755bc85f9b186fb7e2895dda78e805e577fef1011d51d5a5c \ + --hash=sha256:6366ab2107425bf934b0c83311177f2a371bfc757ee8c6ad4a602d7cbcc2f363 \ + --hash=sha256:6421a930b028c5ef4a943b32a5a78b7f1bf15138214525a2088f11acbb7d3d64 \ + --hash=sha256:6609291c41f7ad0bac570bfca5af8fea1f4a27987d30a1fa8b67fe5e67e6c78d \ + --hash=sha256:6a65f844d813ab4ef351443badffa0ae358f98821561d19e18b3190f59e71996 \ + --hash=sha256:6aa11df6c58812f731172b593fcb485d7ba09ccc3b52fea6c7f26a43377dc748 \ + --hash=sha256:6b23d14a7fc0addd9795795763af26b185deb7c456b1e7cc4d5228e69dab5ce8 \ + --hash=sha256:6cad63e3a26556b020b634d25a8703b605c0e0b491426b3e6b9e12ed20f09100 \ + --hash=sha256:6d8b67874f2188399a71a71731e1ba2d1a2c3173b7565d1cc7ffb32e8fbaba5b \ + --hash=sha256:72cca15800ffc00ba25788e4626189fe0bc5fe2a0c1cb4294bce2e4df21cc073 \ + --hash=sha256:7445479ebe7d1aff0ee094ab5a1c7718e1ad78d33e3241e1a1ec65dcdbc22ffb \ + --hash=sha256:7f04febcd70e1e67917be7de513c8d4749d2e09206798558d7fe632134426ea4 \ + --hash=sha256:8066f1b70f21a2961e96bedf48649f27dfd5ea68be5cd1bed3742b047f14acde \ + --hash=sha256:819c8081e2948635cab60c603e1bbdceccdfe19104a242530ad38a36222cb88f \ + --hash=sha256:85b7025795b796dea5284d290ff69de5089fc8e989b25d6f6f15b6800be7167f \ + --hash=sha256:87e74f7d7dfcba9efa91127081e22331d7c42515f0a0ac6e81d4cf2c3ed14661 \ + --hash=sha256:8a434c521fadaf9680788b50d5c21f4048fa85ed19d7d70bd40549fbaeeecab1 \ + --hash=sha256:98fa1ca321c81fb1f02e5c43f956ca543968cc1a30b264fd8e0a2e1b0b0bf106 \ + --hash=sha256:a20c5182af04332cc94d8520792befda06d73daf2865e6dddc5161c72ea92cb9 \ + --hash=sha256:b0d8c364c44ae343937f474b2e492c1040df96d94530377c2f9263fb77096e4f \ + --hash=sha256:b188e626ce61de5ad1f95161b8557beb39253de4ec74fc9b1f25593324a0279c \ + --hash=sha256:b6c1248cc62952a3a005792b10cdef2a4e130847be9c74f33a7d617486f7e532 \ + --hash=sha256:ba9ab2b012fbd53b36cafd8f4440a6b60e7e487cd8b87428e57336b7f38409a4 \ + --hash=sha256:bb9b4077bdf8857b2483879cbbf70f1073bc255b057ec5aac8a70d901bb838e9 \ + --hash=sha256:bdb14bc4d4d83a57062fed2c5da93ecb426ff65b0dc02ddf3481040f5f074a82 \ + --hash=sha256:bff00e1c766658adbd09a175267f8b2f7616e5ee70ce45db3d7c4ce6d9f6bec7 \ + --hash=sha256:c0a0cc80aebd8aa15609dd4d330611cbc05e9b4216bcaeabba7189f99ef07c28 \ + --hash=sha256:c188d06b583900e662cd791a3f962a8c96d3dfc9b36ea315be39e0a4c4792ebf \ + --hash=sha256:c41c76e034a1094afed7057023b1d8967f968782433f7299cd170eaa01ec033e \ + --hash=sha256:c9d7aeafb1b07d25a964b148c0dda9451efb47bbbf67756e16eeae65004b0eb5 \ + --hash=sha256:cb2679ef532f9fa5be5c5a283b6357cb6e9888a8dd889c4bb2b01845a29d8c0b \ + --hash=sha256:da95b38693b989eaa8d32e452e8261cfa77fe5babfef1d8d2ac25af8c4aa7e6d \ + --hash=sha256:e00e275d4ba95d4963431ea3e409aa407566a74ee2bf309a402f84fc744abe47 \ + --hash=sha256:f1472eeafd67cdb22544e59cf3bfc25d23dc94058a68cf41f6654ff4fcb92e09 \ + --hash=sha256:f729c37c9317126da9475bdd06a7208eb52fcbd180a6341648b45a56b4ba708b \ + --hash=sha256:fea5c7fa26556eedf277d4f72779c5ede45ac3018650721edd77fd37ccd4a2d4 + # via pyarrow +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy markupsafe==3.0.3 \ --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ @@ -517,21 +584,21 @@ markupsafe==3.0.3 \ --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 # via jinja2 -maturin==1.12.6 \ - --hash=sha256:06fc8d089f98623ce924c669b70911dfed30f9a29956c362945f727f9abc546b \ - --hash=sha256:2cb41139295eed6411d3cdafc7430738094c2721f34b7eeb44f33cac516115dc \ - --hash=sha256:351f3af1488a7cbdcff3b6d8482c17164273ac981378a13a4a9937a49aec7d71 \ - --hash=sha256:3f32e0a3720b81423c9d35c14e728cb1f954678124749776dc72d533ea1115e8 \ - --hash=sha256:6892b4176992fcc143f9d1c1c874a816e9a041248eef46433db87b0f0aff4278 \ - --hash=sha256:6dbddfe4dc7ddee60bbac854870bd7cfec660acb54d015d24597d59a1c828f61 \ - --hash=sha256:75133e56274d43b9227fd49dca9a86e32f1fd56a7b55544910c4ce978c2bb5aa \ - --hash=sha256:8fdb0f63e77ee3df0f027a120e9af78dbc31edf0eb0f263d55783c250c33b728 \ - --hash=sha256:977290159d252db946054a0555263c59b3d0c7957135c69e690f4b1558ee9983 \ - --hash=sha256:bae91976cdc8148038e13c881e1e844e5c63e58e026e8b9945aa2d19b3b4ae89 \ - --hash=sha256:c0c742beeeef7fb93b6a81bd53e75507887e396fd1003c45117658d063812dad \ - --hash=sha256:d37be3a811a7f2ee28a0fa0964187efa50e90f21da0c6135c27787fa0b6a89db \ - --hash=sha256:e90dc12bc6a38e9495692a36c9e231c4d7e0c9bfde60719468ab7d8673db3c45 \ - --hash=sha256:fa84b7493a2e80759cacc2e668fa5b444d55b9994e90707c42904f55d6322c1e +maturin==1.13.1 \ + --hash=sha256:001741c6cff56aa8ea59a0d78ae990c0550d0e3e82b00b683eedb4158a8ef7e6 \ + --hash=sha256:01c845825c917c07c1d0b2c9032c59c16a7d383d1e649a46481d3e5693c2750f \ + --hash=sha256:2839024dcd65776abb4759e5bca29941971e095574162a4d335191da4be9ff24 \ + --hash=sha256:3da18cccf2f683c0977bff9146a0908d6ffce836d600665736ac01679f588cb9 \ + --hash=sha256:416e4e01cb88b798e606ee43929df897e42c1647b722ef68283816cca99a8742 \ + --hash=sha256:6b1e5916a253243e8f5f9e847b62bbc98420eec48c9ce2e2e8724c6da89d359b \ + --hash=sha256:72888e87819ce546d0d2df900e4b385e4ef299077d92ee37b48923a5602dae94 \ + --hash=sha256:98b5fcf1a186c217830a8295ecc2989c6b1cf50945417adfc15252107b9475b7 \ + --hash=sha256:9a87ff3b8e4d1c6eac33ebfe8e261e8236516d98d45c0323550621819b5a1a2f \ + --hash=sha256:a2017d2281203d0c6570240e7d746564d766d756105823b7de68bda6ae722711 \ + --hash=sha256:c1490584f3c70af45466ee99065b49e6657ebdccac6b10571bb44681309c9396 \ + --hash=sha256:c6a720b252c99de072922dbe4432ab19662b6f80045b0355fec23bdfccb450da \ + --hash=sha256:dc91031e0619c1e28730279ef9ee5f106c9b9ec806b013f888676b242f892eb7 \ + --hash=sha256:f69093ed4a0e6464e52a7fc26d714f859ce15630ec8070743398c6bf41f38a9e # via # cryptography # pydantic-core @@ -612,9 +679,9 @@ numpy==2.2.6 \ # via # pandas # pyarrow -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # hatchling # pyproject-metadata @@ -623,9 +690,9 @@ packaging==26.0 \ # setuptools-scm # vcs-versioning # wheel -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via # hatchling # mypy @@ -654,13 +721,13 @@ poetry-core==2.3.2 \ # pkgconfig # rich # tomlkit -pybind11-global==3.0.3 \ - --hash=sha256:141adb150fdb84f6eba3e27241da886f4582574a3d1c30568bf33c1ed3ec8b82 \ - --hash=sha256:7a75ee81e903ea15bdf05db1342c37400751a72316b6620c800b66d70be45632 +pybind11-global==3.0.4 \ + --hash=sha256:95b693c3d646c6b7217a97156a36b6d40305505d4a5a728082da6fe75ada29f5 \ + --hash=sha256:a73e2ebd29f4ee5d7c754b495d555cd703f2cd26c84abe360ee56411dcd7834d # via pybind11 -pybind11==3.0.3 \ - --hash=sha256:00471cdb816882c484708bc5dde80815c8c11cea540ab2cc6410f5ddea434755 \ - --hash=sha256:fb5f8e4a64946b4dcc0451c83a8c384f803bc0a62dd1ba02f199e97dbc9aad4c +pybind11==3.0.4 \ + --hash=sha256:3286b59c8a774b9ee650169302dd5a4eedc30a8617905a0560dd8ee44775130c \ + --hash=sha256:961720ee652da51d531b7b2451a6bd2bc042b0106e6d9baa48ecb7d58034ce63 # via duckdb pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ @@ -670,12 +737,88 @@ pyproject-metadata==0.11.0 \ --hash=sha256:85bbecca8694e2c00f63b492c96921d6c228454057c88e7c352b2077fcaa4096 \ --hash=sha256:c72fa49418bb7c5a10f25e050c418009898d1c051721d19f98a6fb6da59a66cf # via meson-python +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via libcst scikit-build-core==0.12.2 \ --hash=sha256:562e0bbc9de1a354c87825ccf732080268d6582a0200f648e8c4a2dcb1e3736d \ --hash=sha256:6ea4730da400f9a998ec3287bd3ebc1d751fe45ad0a93451bead8618adbc02b1 # via # duckdb # patchelf + # pyarrow # pybind11 # pybind11-global semantic-version==2.10.0 \ @@ -689,7 +832,9 @@ setuptools-git-versioning==3.0.1 \ setuptools-rust==1.12.1 \ --hash=sha256:85ae70989d96c9cfeb5ef79cf3bac2d5200bc5564f720a06edceedbdf6664640 \ --hash=sha256:b7ebd6a182e7aefa97a072e880530c9b0ec8fcca8617e0bb8ff299c1a064f693 - # via maturin + # via + # libcst + # maturin setuptools-scm==10.0.5 \ --hash=sha256:bbba8fe754516cdefd017f4456721775e6ef9662bd7887fb52ae26813d4838c3 \ --hash=sha256:f611037d8aae618221503b8fa89319f073438252ae3420e01c9ceec249131a0a @@ -701,6 +846,7 @@ setuptools-scm==10.0.5 \ # hatch-vcs # httpx-sse # importlib-metadata + # libcst # pluggy # pyarrow # pybindgen @@ -783,6 +929,7 @@ tomli==2.4.1 \ # fastapi-mcp # flit-scm # frozenlist + # hatch-fancy-pypi-readme # hatchling # maturin # meson-python @@ -792,7 +939,6 @@ tomli==2.4.1 \ # scikit-build-core # setuptools-git-versioning # setuptools-scm - # vcs-versioning # versioneer # yarl tomlkit==0.14.0 \ @@ -807,9 +953,9 @@ types-psutil==7.0.0.20250218 \ --hash=sha256:1447a30c282aafefcf8941ece854e1100eee7b0296a9d9be9977292f0269b121 \ --hash=sha256:1e642cdafe837b240295b23b1cbd4691d80b08a07d29932143cbbae30eb0db9c # via mypy -types-setuptools==82.0.0.20260402 \ - --hash=sha256:4b9a9f6c3c4c65107a3956ad6a6acbccec38e398ff6d5f78d5df7f103dadb8d6 \ - --hash=sha256:63d2b10ba7958396ad79bbc24d2f6311484e452daad4637ffd40407983a27069 +types-setuptools==82.0.0.20260408 \ + --hash=sha256:036c68caf7e672a699f5ebbf914708d40644c14e05298bc49f7272be91cf43d3 \ + --hash=sha256:ece0a215cdfa6463a65fd6f68bd940f39e455729300ddfe61cab1147ed1d2462 # via mypy typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -833,13 +979,14 @@ versioneer==0.29 \ # via # pandas # partd -wheel==0.46.3 \ - --hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d \ - --hash=sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803 +wheel==0.47.0 \ + --hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \ + --hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3 # via # async-timeout # google-crc32c # httpx-sse + # libcst # meson # mmh3 # pandas @@ -883,6 +1030,7 @@ setuptools==80.10.2 \ # gunicorn # httpx-sse # importlib-metadata + # libcst # librt # markupsafe # maturin @@ -901,7 +1049,6 @@ setuptools==80.10.2 \ # psycopg # psycopg-c # psycopg-pool - # pyarrow # pyasn1 # pyasn1-modules # pycparser @@ -925,7 +1072,6 @@ setuptools==80.10.2 \ # tqdm # trove-classifiers # typeguard - # types-pymysql # tzdata # ujson # uvloop @@ -944,4 +1090,5 @@ setuptools==82.0.1 \ --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb # via # python-dateutil + # types-pymysql # types-setuptools diff --git a/sdk/python/requirements/py3.10-minimal-sdist-requirements.txt b/sdk/python/requirements/py3.10-minimal-sdist-requirements.txt index 8b25c5722d1..46546d99171 100644 --- a/sdk/python/requirements/py3.10-minimal-sdist-requirements.txt +++ b/sdk/python/requirements/py3.10-minimal-sdist-requirements.txt @@ -208,9 +208,9 @@ calver==2025.3.31 \ --hash=sha256:07511edf5e7fa75ae97445c8c5921240e0fe62937289a3ebe6963eddd3c691b6 \ --hash=sha256:255d1a70bba8f97dc1eee3af4240ed35980508da69257feef94c79e5c6545fc7 # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # httpcore # httpx @@ -438,9 +438,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -454,56 +454,56 @@ colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via feast (pyproject.toml) -cryptography==46.0.6 \ - --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ - --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ - --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ - --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ - --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ - --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ - --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ - --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ - --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ - --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ - --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ - --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ - --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ - --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ - --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ - --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ - --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ - --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ - --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ - --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ - --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ - --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ - --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ - --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ - --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ - --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ - --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ - --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ - --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ - --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ - --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ - --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ - --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ - --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ - --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ - --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ - --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ - --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ - --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ - --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 +cryptography==47.0.0 \ + --hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \ + --hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \ + --hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \ + --hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \ + --hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \ + --hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \ + --hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \ + --hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \ + --hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \ + --hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \ + --hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \ + --hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \ + --hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \ + --hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \ + --hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \ + --hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \ + --hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \ + --hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \ + --hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \ + --hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \ + --hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \ + --hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \ + --hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \ + --hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \ + --hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \ + --hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \ + --hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \ + --hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \ + --hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \ + --hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \ + --hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \ + --hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \ + --hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \ + --hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \ + --hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \ + --hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \ + --hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \ + --hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \ + --hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \ + --hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \ + --hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \ + --hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \ + --hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \ + --hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \ + --hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \ + --hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \ + --hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \ + --hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \ + --hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736 # via # google-auth # pyjwt @@ -593,42 +593,42 @@ docutils==0.21.2 \ --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \ --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 # via sphinx -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ @@ -640,9 +640,9 @@ exceptiongroup==1.3.1 \ # via # anyio # scikit-build-core -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -650,9 +650,9 @@ fastapi-mcp==0.4.0 \ --hash=sha256:d4a3fe7966af24d44e4b412720561c95eb12bed999a4443a88221834b3b15aec \ --hash=sha256:d4ca9410996f4c7b8ea0d7b20fdf79878dc359ebf89cbf3b222e0b675a55097d # via feast (pyproject.toml) -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via snowflake-connector-python flit-core==3.12.0 \ --hash=sha256:18f63100d6f94385c6ed57a72073443e1a71a4acb4339491615d0f16d6ff01b2 \ @@ -798,9 +798,9 @@ fsspec==2024.9.0 \ # via # feast (pyproject.toml) # dask -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -810,9 +810,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-datastore # google-cloud-storage # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -912,60 +912,66 @@ googleapis-common-protos[grpc]==1.74.0 \ # google-api-core # grpc-google-iam-v1 # grpcio-status -greenlet==3.3.2 \ - --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ - --hash=sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082 \ - --hash=sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b \ - --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ - --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ - --hash=sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727 \ - --hash=sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e \ - --hash=sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2 \ - --hash=sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f \ - --hash=sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327 \ - --hash=sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd \ - --hash=sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2 \ - --hash=sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070 \ - --hash=sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99 \ - --hash=sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be \ - --hash=sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79 \ - --hash=sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7 \ - --hash=sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e \ - --hash=sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf \ - --hash=sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f \ - --hash=sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506 \ - --hash=sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a \ - --hash=sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395 \ - --hash=sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4 \ - --hash=sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca \ - --hash=sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492 \ - --hash=sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab \ - --hash=sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358 \ - --hash=sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce \ - --hash=sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5 \ - --hash=sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef \ - --hash=sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d \ - --hash=sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac \ - --hash=sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55 \ - --hash=sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124 \ - --hash=sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4 \ - --hash=sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986 \ - --hash=sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd \ - --hash=sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f \ - --hash=sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb \ - --hash=sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4 \ - --hash=sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13 \ - --hash=sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab \ - --hash=sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff \ - --hash=sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a \ - --hash=sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9 \ - --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 \ - --hash=sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd \ - --hash=sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71 \ - --hash=sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92 \ - --hash=sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643 \ - --hash=sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54 \ - --hash=sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9 +greenlet==3.4.0 \ + --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \ + --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \ + --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \ + --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \ + --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \ + --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \ + --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \ + --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \ + --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \ + --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \ + --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \ + --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \ + --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \ + --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \ + --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \ + --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \ + --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \ + --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \ + --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \ + --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \ + --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \ + --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \ + --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \ + --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \ + --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \ + --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \ + --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \ + --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \ + --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \ + --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \ + --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \ + --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \ + --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \ + --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \ + --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \ + --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \ + --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \ + --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \ + --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \ + --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \ + --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \ + --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \ + --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \ + --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \ + --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \ + --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \ + --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \ + --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \ + --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \ + --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \ + --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \ + --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \ + --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \ + --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \ + --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \ + --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \ + --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \ + --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \ + --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf # via feast (pyproject.toml) grpc-google-iam-v1==0.14.4 \ --hash=sha256:392b3796947ed6334e61171d9ab06bf7eb357f554e5fc7556ad7aab6d0e17038 \ @@ -1236,9 +1242,9 @@ ibis-framework[duckdb]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1280,97 +1286,97 @@ kubernetes==35.0.0 \ --hash=sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d \ --hash=sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee # via feast (pyproject.toml) -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -1749,51 +1755,51 @@ multidict==6.7.1 \ # aiobotocore # aiohttp # yarl -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -1866,9 +1872,9 @@ oauthlib==3.3.1 \ --hash=sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9 \ --hash=sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1 # via requests-oauthlib -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # db-dtypes @@ -1948,9 +1954,9 @@ pandas==2.3.3 \ # pandas-gbq # pymilvus # snowflake-connector-python -pandas-gbq==0.34.1 \ - --hash=sha256:6bea5b85937251b976cf9db38151ea59abbff98771179183488d4614694bff67 \ - --hash=sha256:b74932c6ee35dfc81582f39c792e3a68c9ef9bee8c85f25667d9d05dfadd0daf +pandas-gbq==0.35.0 \ + --hash=sha256:258de481019566611031919997bf9c1ece4ca30a4dd02d3fc3664b251d446182 \ + --hash=sha256:596c908487ef0649a161e86ef272c00c267e581b95dc5ee0dc3518545b33bcfc # via google-cloud-bigquery parsy==2.2 \ --hash=sha256:5e981613d9d2d8b68012d1dd0afe928967bea2e4eefdb76c2f545af0dd02a9e7 \ @@ -1971,24 +1977,24 @@ patchelf==0.17.2.4 \ --hash=sha256:d842b51f0401460f3b1f3a3a67d2c266a8f515a5adfbfa6e7b656cb3ac2ed8bc \ --hash=sha256:d9b35ebfada70c02679ad036407d9724ffe1255122ba4ac5e4be5868618a5689 # via feast (pyproject.toml) -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via # hatchling # mypy # scikit-build-core -platformdirs==4.9.4 \ - --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ - --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 +platformdirs==4.9.6 \ + --hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \ + --hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917 # via snowflake-connector-python pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 # via hatchling -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) propcache==0.4.1 \ --hash=sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e \ @@ -2184,57 +2190,57 @@ psycopg-pool==3.3.0 \ --hash=sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 \ --hash=sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5 # via psycopg -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -2263,141 +2269,140 @@ pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi # fastapi-mcp # mcp # pydantic-settings -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # fastapi-mcp # mcp @@ -2427,9 +2432,9 @@ pymysql==1.1.2 \ --hash=sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03 \ --hash=sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9 # via feast (pyproject.toml) -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python pyproject-metadata==0.11.0 \ --hash=sha256:85bbecca8694e2c00f63b492c96921d6c228454057c88e7c352b2077fcaa4096 \ @@ -2452,9 +2457,9 @@ python-dotenv==1.2.2 \ # pydantic-settings # pymilvus # uvicorn -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp pytz==2026.1.post1 \ --hash=sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1 \ @@ -2570,9 +2575,9 @@ requests-oauthlib==2.0.0 \ # via # google-auth-oauthlib # kubernetes -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 # via # fastapi-mcp # ibis-framework @@ -2790,74 +2795,74 @@ sphinxcontrib-serializinghtml==2.0.0 \ --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \ --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d # via sphinx -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via ibis-framework sse-starlette==3.3.4 \ --hash=sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1 \ @@ -2965,17 +2970,17 @@ typeguard==4.5.1 \ --hash=sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40 \ --hash=sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274 # via feast (pyproject.toml) -typer==0.24.1 \ - --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e \ - --hash=sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45 +typer==0.24.2 \ + --hash=sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8 \ + --hash=sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480 # via fastapi-mcp types-psutil==7.0.0.20250218 \ --hash=sha256:1447a30c282aafefcf8941ece854e1100eee7b0296a9d9be9977292f0269b121 \ --hash=sha256:1e642cdafe837b240295b23b1cbd4691d80b08a07d29932143cbbae30eb0db9c # via feast (pyproject.toml) -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -3014,9 +3019,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # ibis-framework # pandas @@ -3565,9 +3570,9 @@ yarl==1.23.0 \ --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \ --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d # via aiohttp -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata # The following packages were excluded from the output: diff --git a/sdk/python/requirements/py3.10-requirements.txt b/sdk/python/requirements/py3.10-requirements.txt index aaf668ef6ec..166b2a0edd8 100644 --- a/sdk/python/requirements/py3.10-requirements.txt +++ b/sdk/python/requirements/py3.10-requirements.txt @@ -24,9 +24,9 @@ bigtree==1.4.0 \ --hash=sha256:d0d99550ae64ce4529f132602ab875c2ab472c96c942f5704f8c72a17450d3ea \ --hash=sha256:e5ae2e948168da671d99601c9ed87ab3b48d9d4ea8a98f111e5748e98064c31c # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via requests charset-normalizer==3.4.7 \ --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \ @@ -159,9 +159,9 @@ charset-normalizer==3.4.7 \ --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \ --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464 # via requests -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -186,9 +186,9 @@ exceptiongroup==1.3.1 \ --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 # via anyio -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via feast (pyproject.toml) fsspec==2026.3.0 \ --hash=sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41 \ @@ -249,9 +249,9 @@ httptools==0.7.1 \ --hash=sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec \ --hash=sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362 # via uvicorn -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # requests @@ -271,97 +271,97 @@ jsonschema-specifications==2025.9.1 \ --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d # via jsonschema -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -567,51 +567,51 @@ mmh3==5.2.1 \ --hash=sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a \ --hash=sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b # via feast (pyproject.toml) -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -677,9 +677,9 @@ numpy==2.2.6 \ # feast (pyproject.toml) # dask # pandas -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # gunicorn @@ -746,13 +746,13 @@ partd==1.4.2 \ --hash=sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f \ --hash=sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c # via dask -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via mypy -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) protobuf==7.34.1 \ --hash=sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a \ @@ -787,188 +787,187 @@ psutil==7.2.2 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 # via feast (pyproject.toml) -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic pygments==2.20.0 \ --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ @@ -1201,70 +1200,70 @@ six==1.17.0 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 # via python-dateutil -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) starlette==1.0.0 \ --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \ @@ -1368,9 +1367,9 @@ typing-inspection==0.4.2 \ # via # fastapi # pydantic -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via pandas urllib3==2.6.3 \ --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ @@ -1611,7 +1610,7 @@ websockets==16.0 \ --hash=sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da \ --hash=sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4 # via uvicorn -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata diff --git a/sdk/python/requirements/py3.11-ci-requirements.txt b/sdk/python/requirements/py3.11-ci-requirements.txt index 2d496a89f36..eba75d46f96 100644 --- a/sdk/python/requirements/py3.11-ci-requirements.txt +++ b/sdk/python/requirements/py3.11-ci-requirements.txt @@ -370,49 +370,44 @@ botocore==1.38.46 \ # moto # s3transfer # snowflake-connector-python -build==1.4.2 \ - --hash=sha256:35b14e1ee329c186d3f08466003521ed7685ec15ecffc07e68d706090bf161d1 \ - --hash=sha256:7a4d8651ea877cb2a89458b1b198f2e69f536c95e89129dbf5d448045d60db88 +build==1.4.4 \ + --hash=sha256:8c3f48a6090b39edec1a273d2d57949aaf13723b01e02f9d518396887519f64d \ + --hash=sha256:f832ae053061f3fb524af812dc94b8b84bac6880cd587630e3b5d91a6a9c1703 # via # feast (pyproject.toml) # pip-tools # singlestoredb -cassandra-driver==3.29.3 \ - --hash=sha256:064bf45d3ca87239e11168c0110676fc64f7fdbddb4bcba9be787b8ad5f6d734 \ - --hash=sha256:0785f6e0986089e922378ae3b64b5f696440aeb595fb84c2cf3ccef220c6ae91 \ - --hash=sha256:158f7e5cb894a76a592aa0ca659a8e7c2a57ef603e04c07bbbc289a70e9ac893 \ - --hash=sha256:1c241ba08473baf31a333feb59793190d01625541c2368d3bbb0f43a586f1d6a \ - --hash=sha256:26013d768b2ea4728c09144b08c0eb86ad692e85cb15f4e52e3107abca83683c \ - --hash=sha256:27adf8869937461ad08c5fefb47857532e467b408db496db4dbf8b132a4bd623 \ - --hash=sha256:281f67af1b8df88741eef551afbb49f78e4f366a7ab23e7060a1f0d6ba655752 \ - --hash=sha256:29fc241475801872dc27c3dd1a3976373536223dd4fd1c01868ff86bdbbfd48b \ - --hash=sha256:2b72312a8b62a905da6133effbba9b0731c8e30af96e10ca77fc5c34532c6827 \ - --hash=sha256:2cb72808dfc46c40a6ee352ace181ce3170adde1cfd1447da91709a8cf482e20 \ - --hash=sha256:38216e13d6f2e0d4513a5b8806e70ce4a8f28a82962793a67371582fc2c7141b \ - --hash=sha256:3f654b01d8d49f68deedfaff1edcff314e3103d29130b2a034df6c490c522351 \ - --hash=sha256:51d6a5390e2454b599500049f0a5c72aa701db155c1e542f9a1157c1c45814b1 \ - --hash=sha256:54afde4aaa5b55fbc2c075e1c55fb14a5739459428f3bb81f849ad020f7d5bcf \ - --hash=sha256:572bd5a01089ab92da12f4f52b32b878547bbc544a798d8cfd042e7fc2601c75 \ - --hash=sha256:5a0113020d86e8f61c7a2ae3d508720cd036df7462a55926b85dd97ada27e143 \ - --hash=sha256:5f9858b5ccdf75dd89c20d74474b59dd3a2e2f86c7251b310011c46acdef3874 \ - --hash=sha256:638047c1f70fb14c9d8f743931d4f4f42aff6793b47afded3097c002ef8c1165 \ - --hash=sha256:63adca0f9219be3fe8789f4aa7b77c5f6a7bf65d6442959db52c653140ca4185 \ - --hash=sha256:7552fb7189acd06161f8feac7045a387dc9e03b3b9f7dcb5675178906cee792e \ - --hash=sha256:7a2f371af54cd1d153ef373a733889ebfbcc9c30e00429fc12a2569bad9239e1 \ - --hash=sha256:84b24f69a7bbe76302330d47422a7fcc1998a6a96ffd414a795d7d95992b49cb \ - --hash=sha256:891a1b6a111a591ad9f1c9e088846848dc9e6be030a6086c8c3aa5d2d837f266 \ - --hash=sha256:96ad742f5cbfb771df512959ab5de36e248ce9aa2c487fd81c37d5c0a627c094 \ - --hash=sha256:9abedc832e9a6636741299aae46c032d8c1248b507d8cebbaa2f48ec202904bc \ - --hash=sha256:9b7032b44769c454e96aa11483bfd167a87ea341268f1075b0ff84f780c910a9 \ - --hash=sha256:c935431682557ffcd3efc1c7bcb01b0f6769a1c90751a7154d5e3c905a6a2042 \ - --hash=sha256:e1d09691d757f5b1900a98cc3b6cc7d8506683a2188c01eca86545f91edbbaf5 \ - --hash=sha256:facd488c2b9be8bffcad5903566581e96d2863d2ec4bcad7f114d1b2b2f39ad0 \ - --hash=sha256:fcf45725ae1751cb934b9b827a7d9cd899bbd09eb1ad28e2160b4584de35ba77 \ - --hash=sha256:ff6b82ee4533f6fd4474d833e693b44b984f58337173ee98ed76bce08721a636 +cassandra-driver==3.30.0 \ + --hash=sha256:0c28a8e84917acebecbaed39844047c2f135739c3627dd7b9f8541af33e11df3 \ + --hash=sha256:0f4225082a11d9529416c223553ab38a29c4e65da6646b40159c554480dc002c \ + --hash=sha256:136b46437b9902673264e101cdaab309d3e40607bff34430bda86b785badc6e4 \ + --hash=sha256:137498e2a9b6f578d1902e1af8a988e50b8fe134c76a176f1b8a774e906bc66c \ + --hash=sha256:17fb53587c9fc6a27b5c4a89b4f3d9169be43fc572d6f3f67494aa74708be936 \ + --hash=sha256:1d64cbdce764c33e284d339b9a749736d68971edf8b537888f2d13c4b0d1313f \ + --hash=sha256:212af4d8ff934c30538f4bdf7da61f14dc9a30349f6cac2161c8125e56fad928 \ + --hash=sha256:2637644eac9274e46b0c2a7f729158bdf8582b6842dc48e18297211dd3ee1fec \ + --hash=sha256:289e86c81be2543cb9055600c0819850db921e6e138a84e5c88ec160662c7207 \ + --hash=sha256:2a0679ebcfdcecb3763c690b5bc6a517e0c0803f7bc88e0a6c793e5e421b558a \ + --hash=sha256:385134eba72f048707cd800de0a61cf3c23246113edffe9bc6bc2eb86282d26b \ + --hash=sha256:5c6cbb396ad6fe456efc799d3b8b6bda360ffc06552c5be2ce1a88ac381a305c \ + --hash=sha256:61d7eeb17d8f76d5b4a9b1239145250f2a9f7bf949c30e2cc36196b5a0523ce1 \ + --hash=sha256:6a5c8982f2b9eb4e789fc12cdd930b1e1511b6d046dde31d0703f855745556a3 \ + --hash=sha256:6d449f49ce866ac20a1c3d80b1f9245ecdfd1e67b843dccd3d6eccdfe519c02e \ + --hash=sha256:7e4cfd6ec3023576ed0ffa34882d9778e4bacfd918048ae9139ccdd00628ed85 \ + --hash=sha256:83a9148d408a3dbb48ea1802d643d60fa53cd69dc7b9a244511ecf5b917e4f53 \ + --hash=sha256:8c4acd28791854c23ca68be50a7a750c9413ba80fec0ca5c27c2be05f6f3fe0a \ + --hash=sha256:8d5e3575ec01d8c043b56ff25de6f61ff4c9ed5cb3ea4c3d9df98def71ba710c \ + --hash=sha256:923a6e1c3fa5f98f846a028b1a7207ec9e7d8cfa54ea47a507d41122efa2f54f \ + --hash=sha256:c1b4aa6c7706dec839134adb6a2094d90c5f6f35efa08028ed6aae6e67c8643e \ + --hash=sha256:c64e20bf46b49f8ef64569208d4a395b0928c27d5960559922a2d13471924d0d \ + --hash=sha256:d2f9e00127f70dff42d4ef932df8a6b81170c2861d4e75c8b13f4b4816b4450c \ + --hash=sha256:d73c0429813045ba86b92fc033fbcfd495aa10e9d4a40fe30b6e9dfe8b5d3ab4 \ + --hash=sha256:e12dfcd3f0074c16f4bfe650242edb406b935864373ae86160e09e3f5e437e84 \ + --hash=sha256:ff2e9fbdc1be54c1d041ea3f7d09812442f334be14bb5ad7aede175544765d25 # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # clickhouse-connect # docling @@ -650,9 +645,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -1010,9 +1005,9 @@ db-dtypes==1.5.1 \ # via # google-cloud-bigquery # pandas-gbq -dbt-artifacts-parser==0.13.0 \ - --hash=sha256:304f2b857650566fed4ed8b976ed3582332eda3cedfe7167158dbbcfced3fe47 \ - --hash=sha256:55498e8bd0d9064d56617f9c714ced8607d94ccb61e70d4b49dcfd8a28a030d8 +dbt-artifacts-parser==0.13.1 \ + --hash=sha256:c341730fa34ebb38cc7d2de0282e8b713e2fc65fc6577f0d944f8abee8949dc4 \ + --hash=sha256:c7a3c4e309ae2d7d566a615e92043b0d346a77998203b0cc466234717b806e40 # via feast (pyproject.toml) debugpy==1.8.20 \ --hash=sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad \ @@ -1065,6 +1060,10 @@ deltalake==0.25.5 \ --hash=sha256:cb1c7e826fd7c3bdd3676c7471d3b551e1a3674e44cd8e3747a0017a2c0292b7 \ --hash=sha256:e8f0d24bf64455f702da8402307b22e01f91e0f76694f7c5e33c9513011e8d29 # via feast (pyproject.toml) +deprecated==1.3.1 \ + --hash=sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f \ + --hash=sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223 + # via cassandra-driver deprecation==2.1.0 \ --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a @@ -1080,6 +1079,12 @@ distlib==0.4.0 \ --hash=sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 \ --hash=sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d # via virtualenv +dnspython==2.8.0 \ + --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \ + --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f + # via + # feast (pyproject.toml) + # pymongo docker==7.1.0 \ --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \ --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0 @@ -1088,16 +1093,16 @@ docling==2.27.0 \ --hash=sha256:1288ed75b27e33bf94daff34faffc6d11b7d7ccc13e3df84fb24adad3991f72d \ --hash=sha256:faba35662612a2c687a3a463e501d95f645316436084af92a0442ce162429a3d # via feast (pyproject.toml) -docling-core[chunking]==2.71.0 \ - --hash=sha256:4761857816853b2b35263b5b4518e1ea6214e0565db0bbf1d929fb976665d1a0 \ - --hash=sha256:4caa9f50c68b9dd332584ae16170b36db05d773532b14d7078b580d89d8bd2a4 +docling-core[chunking]==2.74.1 \ + --hash=sha256:46bf298686f2c51ddd69b6935a27dff1cc80838f2f5f1a8823492d99cf1a357b \ + --hash=sha256:e6464078012b3d45f4e0accd101fcb277063903f355eabbb9aee8de00527a789 # via # docling # docling-ibm-models # docling-parse -docling-ibm-models==3.13.0 \ - --hash=sha256:a11acc6034b06e0bed8dc0ca1fa700615b8246eacce411619168e1f6562b0d0d \ - --hash=sha256:f402effae8a63b0e5c3b5ce13120601baa2cd8098beef1d53ab5a056443758d3 +docling-ibm-models==3.13.2 \ + --hash=sha256:195e02dd119df34d2ce5f76ac614da82825851013e4898db7b0468cdf8740a3d \ + --hash=sha256:5fa0838bf15a4e06d2fcb686d756a6f4c329ea0a8820d085f06d07abe96269ed # via docling docling-parse==4.7.3 \ --hash=sha256:1790e7e4ae202d67875c1c48fd6f8ef5c51d10b0c23157e4989b8673f2f31308 \ @@ -1132,42 +1137,42 @@ docutils==0.19 \ --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc # via sphinx -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ @@ -1229,9 +1234,9 @@ faiss-cpu==1.10.0 \ --hash=sha256:e71f7e24d5b02d3a51df47b77bd10f394a1b48a8331d5c817e71e9e27a8a75ac \ --hash=sha256:f71c5860c860df2320299f9e4f2ca1725beb559c04acb1cf961ed24e6218277a # via feast (pyproject.toml) -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -1243,9 +1248,9 @@ fastjsonschema==2.21.2 \ --hash=sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463 \ --hash=sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de # via nbformat -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via # datasets # huggingface-hub @@ -1410,9 +1415,9 @@ geomet==1.1.0 \ --hash=sha256:4372fe4e286a34acc6f2e9308284850bd8c4aa5bc12065e2abbd4995900db12f \ --hash=sha256:51e92231a0ef6aaa63ac20c443377ba78a303fd2ecd179dc3567de79f3c11605 # via cassandra-driver -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -1423,9 +1428,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-storage # opencensus # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -1906,13 +1911,13 @@ ibis-framework[duckdb, mssql, oracle]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -identify==2.6.18 \ - --hash=sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd \ - --hash=sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737 +identify==2.6.19 \ + --hash=sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a \ + --hash=sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842 # via pre-commit -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1934,17 +1939,17 @@ importlib-metadata==8.7.1 \ # via # dask # opentelemetry-api -importlib-resources==6.5.2 \ - --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ - --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec +importlib-resources==7.1.0 \ + --hash=sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708 \ + --hash=sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1 # via happybase iniconfig==2.3.0 \ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 # via pytest -invoke==2.2.1 \ - --hash=sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8 \ - --hash=sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707 +invoke==3.0.3 \ + --hash=sha256:437b6a622223824380bfb4e64f612711a6b648c795f565efc8625af66fb57f0c \ + --hash=sha256:f11327165e5cbb89b2ad1d88d3292b5113332c43b8553b494da435d6ec6f5053 # via paramiko ipykernel==7.2.0 \ --hash=sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e \ @@ -2061,9 +2066,9 @@ jupyter-core==5.9.1 \ # nbclient # nbconvert # nbformat -jupyter-events==0.12.0 \ - --hash=sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb \ - --hash=sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b +jupyter-events==0.12.1 \ + --hash=sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf \ + --hash=sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3 # via jupyter-server jupyter-lsp==2.3.1 \ --hash=sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81 \ @@ -2100,9 +2105,9 @@ jupyterlab-widgets==3.0.16 \ --hash=sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0 \ --hash=sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8 # via ipywidgets -jwcrypto==1.5.6 \ - --hash=sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789 \ - --hash=sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039 +jwcrypto==1.5.7 \ + --hash=sha256:70204d7cca406eda8c82352e3c41ba2d946610dafd19e54403f0a1f4f18633c6 \ + --hash=sha256:729463fefe28b6de5cf1ebfda3e94f1a1b41d2799148ef98a01cb9678ebe2bb0 # via python-keycloak kube-authkit==0.4.0 \ --hash=sha256:1df61ac392fca96c8f5ae8c3d6e9918f1e1655d212434b3c3da5f92cc23b660d \ @@ -2119,9 +2124,9 @@ lark==1.3.1 \ --hash=sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905 \ --hash=sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12 # via rfc3987-syntax -latex2mathml==3.79.0 \ - --hash=sha256:11bde318c2d2d6fcdd105a07509d867cee2208f653278eb80243dec7ea77a0ce \ - --hash=sha256:9f10720d4fcf6b22d1b81f6628237832419a7a29783c13aa92fa8d680165e63d +latex2mathml==3.81.0 \ + --hash=sha256:4b959cdc3cac8686bc0e3e5aece8127dfb1b81ca1241bed8e00ef31b82bb4022 \ + --hash=sha256:d317710393fe20579aea39cfe8928fa2ad9b8780896e585326c75e89c1d1d1a4 # via docling-core lazy-loader==0.5 \ --hash=sha256:717f9179a0dbed357012ddad50a5ad3d5e4d9a0b8712680d4e687f5e6e6ed9b3 \ @@ -2596,9 +2601,9 @@ mpmath==1.3.0 \ --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c # via sympy -msal==1.35.1 \ - --hash=sha256:70cac18ab80a053bff86219ba64cfe3da1f307c74b009e2da57ef040eb1b5656 \ - --hash=sha256:8f4e82f34b10c19e326ec69f44dc6b30171f2f7098f3720ea8a9f0c11832caa3 +msal==1.36.0 \ + --hash=sha256:36ecac30e2ff4322d956029aabce3c82301c29f0acb1ad89b94edcabb0e58ec4 \ + --hash=sha256:3f6a4af2b036b476a4215111c4297b4e6e236ed186cd804faefba23e4990978b # via # azure-identity # msal-extensions @@ -2884,9 +2889,9 @@ nbclient==0.10.4 \ --hash=sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9 \ --hash=sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440 # via nbconvert -nbconvert==7.17.0 \ - --hash=sha256:1b2696f1b5be12309f6c7d707c24af604b87dfaf6d950794c7b07acab96dda78 \ - --hash=sha256:4f99a63b337b9a23504347afdab24a11faa7d86b405e5c8f9881cd313336d518 +nbconvert==7.17.1 \ + --hash=sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2 \ + --hash=sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8 # via jupyter-server nbformat==5.10.4 \ --hash=sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a \ @@ -3063,8 +3068,8 @@ opencv-python-headless==4.13.0.92 \ --hash=sha256:a7cf08e5b191f4ebb530791acc0825a7986e0d0dee2a3c491184bd8599848a4b \ --hash=sha256:eb60e36b237b1ebd40a912da5384b348df8ed534f6f644d8e0b4f103e272ba7d # via easyocr -openlineage-python==1.45.0 \ - --hash=sha256:cf66e7d517d3c8b510b39ad646d8fd0ca2f0cc92d7d6d601d93b2a859783f380 +openlineage-python==1.46.0 \ + --hash=sha256:f6228a01d34990e76ede5b55b3f99169e54e2e624814c4493f064b9cb1bfba37 # via feast (pyproject.toml) openpyxl==3.1.5 \ --hash=sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2 \ @@ -3074,30 +3079,30 @@ openshift-client==1.0.18 \ --hash=sha256:be3979440cfd96788146a3a1650dabe939d4d516eea0b39f87e66d2ab39495b1 \ --hash=sha256:d8a84080307ccd9556f6c62a3707a3e6507baedee36fa425754f67db9ded528b # via codeflare-sdk -opentelemetry-api==1.40.0 \ - --hash=sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f \ - --hash=sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9 +opentelemetry-api==1.41.1 \ + --hash=sha256:0ad1814d73b875f84494387dae86ce0b12c68556331ce6ce8fe789197c949621 \ + --hash=sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f # via # opentelemetry-exporter-prometheus # opentelemetry-sdk # opentelemetry-semantic-conventions -opentelemetry-exporter-prometheus==0.61b0 \ - --hash=sha256:3013b41f4370143d48d219a2351473761423e5882fa4c213811eaefacba39cb7 \ - --hash=sha256:7c4919bd8e79abd62b610767e80f42c9c3a06c5183f4dd9141eedeb57aea284b +opentelemetry-exporter-prometheus==0.62b1 \ + --hash=sha256:7a0b8a6402e107e1f93e38f074a668797e1103936b189561959531a67ffeba55 \ + --hash=sha256:7ecbac9aa76e7abb44082ab0ff2983e0a573e4091c4653f7db483b02bae03506 # via ray opentelemetry-proto==1.27.0 \ --hash=sha256:33c9345d91dafd8a74fc3d7576c5a38f18b7fdf8d02983ac67485386132aedd6 \ --hash=sha256:b133873de5581a50063e1e4b29cdcf0c5e253a8c2d8dc1229add20a4c3830ace # via ray -opentelemetry-sdk==1.40.0 \ - --hash=sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2 \ - --hash=sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1 +opentelemetry-sdk==1.41.1 \ + --hash=sha256:724b615e1215b5aeacda0abb8a6a8922c9a1853068948bd0bd225a56d0c792e6 \ + --hash=sha256:edee379c126c1bce952b0c812b48fe8ff35b30df0eecf17e98afa4d598b7d85d # via # opentelemetry-exporter-prometheus # ray -opentelemetry-semantic-conventions==0.61b0 \ - --hash=sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a \ - --hash=sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2 +opentelemetry-semantic-conventions==0.62b1 \ + --hash=sha256:c5cc6e04a7f8c7cdd30be2ed81499fa4e75bfbd52c9cb70d40af1f9cd3619802 \ + --hash=sha256:cf506938103d331fbb78eded0d9788095f7fd59016f2bda813c3324e5a74a93c # via opentelemetry-sdk oracledb==3.4.2 \ --hash=sha256:00c79448017f367bb7ab6900efe0706658a53768abea2b4519a4c9b2d5743890 \ @@ -3212,9 +3217,9 @@ overrides==7.7.0 \ --hash=sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a \ --hash=sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49 # via jupyter-server -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # accelerate # build @@ -3505,9 +3510,9 @@ pre-commit==3.3.1 \ --hash=sha256:218e9e3f7f7f3271ebc355a15598a4d3893ad9fc7b57fe446db75644543323b9 \ --hash=sha256:733f78c9a056cdd169baa6cd4272d51ecfda95346ef8a89bf93712706021b907 # via feast (pyproject.toml) -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via # feast (pyproject.toml) # jupyter-server @@ -3826,57 +3831,57 @@ py4j==0.10.9.9 \ --hash=sha256:c7c26e4158defb37b0bb124933163641a2ff6e3a3913f7811b0ddbe07ed61533 \ --hash=sha256:f694cad19efa5bd1dee4f3e5270eb406613c974394035e5bfc4ec1aba870b879 # via pyspark -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -3989,9 +3994,9 @@ pycryptodome==3.23.0 \ --hash=sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be \ --hash=sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7 # via minio -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # codeflare-sdk @@ -4007,134 +4012,134 @@ pydantic==2.12.5 \ # pydantic-settings # qdrant-client # ray -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # docling + # docling-core # fastapi-mcp # mcp pydata-google-auth==1.9.1 \ @@ -4169,6 +4174,79 @@ pymilvus==2.5.18 \ --hash=sha256:1b78badcfa8d62db7d0b29193fc0422e4676873ff1c745a9d75c2c885d7a7e32 \ --hash=sha256:9e517076068e98dac51c018bc0dfe1f651d936154e2e2d9ad6c7b3dab1164e2d # via feast (pyproject.toml) +pymongo==4.17.0 \ + --hash=sha256:0ff6bd2f735ab5356541e3e57d5b7dbfbc3f2ee1ccb10b6b0f82d58af69d1d8e \ + --hash=sha256:1175563375d682260f613a96fb7a53dce746ed752bfd924eab61de3bc5bfde34 \ + --hash=sha256:1195370a77baf003b59b10e91ecc4706297197f0dd9d29c840cc556dc08f7cee \ + --hash=sha256:12c4fded3a9f1d6a687e36ebd384ac6d00b9b00de1969aa74048e7051ec2a713 \ + --hash=sha256:15d3f3d732aecac1f8d481bde4029755615639bd3076f258a2147210aec8515a \ + --hash=sha256:20323b0b1c1d33770ad1fc68d429c757734ce9ad3594421c3d6618f10572b1b9 \ + --hash=sha256:2a0d5ac205728c86e0a02192f1aa5f865b0d7d51f8df6101c01a69a7fc620d72 \ + --hash=sha256:2db66aa8dd253a0fc1fad3b0d23d5b3993f7ebde02fbbd7727128debf2853675 \ + --hash=sha256:2e190827834fce70ecdf9d46796c6dbc0ce08ea87dc2ff5bc6f3f5579b605cb9 \ + --hash=sha256:320b34457b20bbcc79997801f95d25ce00472915ca5241167242b42c4359e027 \ + --hash=sha256:3689ea34f6b647c7d1e7bdc60fcfb214b2789ed1359a7fb96569c69f50e5f18f \ + --hash=sha256:37a8385c29881b43eab31f584100fa0eaddedd5607adf010147ba1810118be90 \ + --hash=sha256:3987e96e7c7be4083d42e8ac2cc6c0d5b78db9973c90fce42ae800b616ca6b20 \ + --hash=sha256:4141e6c6a339789b2974efa00ecd9409101672d77a0e3ee2cc3839eedf8ec4df \ + --hash=sha256:422fa50d7d7f5c22ea0953554396c9ef95684a2d775f860bd75a7b510538dfca \ + --hash=sha256:47b021363cd923ace5edc7a1d63c0ff8a6d9d43859b8a1ba23645f5afae63221 \ + --hash=sha256:485c8a8eaa4c739f00a331fc73757898ee7c092c214a79e63866ff76aaf282ff \ + --hash=sha256:48bbc576677b50af043df870d84ded67cc3a9b4aa7553201beef4da5dc050a0a \ + --hash=sha256:4ae22fafca69dd3c78261969e999782ac5fc23b76cf8cccfbc3707982a74cc3d \ + --hash=sha256:50e8f8e23c6df7c6d6929f5e734980b227706e73ee847517c9ba5af90f7fc466 \ + --hash=sha256:51e1915761f65f2aaabd0ba691a31d56551d3f19d1263c2d6bf261730603de5f \ + --hash=sha256:5376ad67bb30ae910d83affcf997f706d9dee37e8b5dad8b6fedb0626e262d85 \ + --hash=sha256:5960519b4d7168f1ecdd3ea10c81b2aedeb9423651aca953cfbc8e76705d3b38 \ + --hash=sha256:5a5de048e6da5c18e27cc2437e8c15b3b0cdc8385c15b41178b0caa3322a09c2 \ + --hash=sha256:5ab3b8ff79e0dfc49b68f3c925e8cc735ea95c60efaed84cfe75692dffcaac2a \ + --hash=sha256:64837adbbd72073301af51bb0fc80e3d7707fe5527cea1033ba0320f0b2f881b \ + --hash=sha256:6877214bff5f06f6884a9fc8d9016a4a7a5f51f537f5c51ac3a576f93e7dfb32 \ + --hash=sha256:68fca71e05ee5da23a8d73cee8379dfb3d26e609a377cae731d742771ed96946 \ + --hash=sha256:6c5f62862d0f87be481fa1fe8cb811994486773c94a2b61e509285e3f2890763 \ + --hash=sha256:6fe0de9d0f6791abce3471230b32b4817bf89d27b1182b6a550e1ec0fa72aa9a \ + --hash=sha256:70ffa08ba641468cc068cf46c06b34f01a8ce3489f6411309fcb5ceabe6b2fc0 \ + --hash=sha256:757f2a4c0c2c46cab87df0333681ce69e86c9d5b45bc5203ceba5410b3489e59 \ + --hash=sha256:75bc3aa5b94fdb7138d357ec6ca61cd97e0c79f4f7f0bd3efe9639b15cc50942 \ + --hash=sha256:77aa4bc164b4de60d5db193b322f0f5b6ead716e831031bfdef8e8bd92205556 \ + --hash=sha256:7db10678814cdf7ea39fd308c6f41395cfa7b29d904bcd7895288963d8f892ba \ + --hash=sha256:809ec74de3b9148ae43fa8df9faf53470f511c8d384f13b99d6f671f2a379f15 \ + --hash=sha256:8446ff4bfcb6ec2a2e50998c860986a1e992136f998b7f53e7a717fb8aa5a0b9 \ + --hash=sha256:8a1be016198a03fd7727cdd55998964bfa4e5a6fd9733c8e95830628cef34d29 \ + --hash=sha256:8e97e03fa13327c87e3fdc5656acd01e71817f0c1dc3221cd8f30de136bf4ec3 \ + --hash=sha256:93641192644fa1ee0f34030e774fd31022a27ad11ba22cb1716142231524f8bd \ + --hash=sha256:9543d8f84c2e5608565c08ac679774811e6730770d8a645439b073422a4276fb \ + --hash=sha256:9828485f72f63c7d802e0ec41f71906f633c2692621ab3af55ca990186b091b1 \ + --hash=sha256:9eb5d63a3c518cb0804ed678f5e2b875af032d89a7cf57a57360322cf6a4d222 \ + --hash=sha256:a431b737816bf4cddd4fa0fcef04e424ad36b7692734a64150f872fb8f3208be \ + --hash=sha256:a8f9c40a09bb7d4b9fc8b1da65ecf6efa79bda5cb2756f39d9b6940fac1d19ae \ + --hash=sha256:addd0498ebbdc6354227f6ed457ed9fce442d48a3bb30d5b5bad33e104996561 \ + --hash=sha256:b24598dc3c2feccbc83b43044be48145a0dc4f9bee49ef923e3d707d54a55d85 \ + --hash=sha256:b2dfcc795f5b9fedbe179a11fdf6051581479d196582a3fe819a92a00e9b9969 \ + --hash=sha256:b4384700cffc3f1dd98e088bc0072dedf6d7d68a230bb4b972665cf69c071c1e \ + --hash=sha256:b93b22eedc62598cf5ee9d8c8007a8e9121c50fd88137012d8985500e9dc3151 \ + --hash=sha256:ba2195d4f386f839a52a23ea1cfd60ffaaba78a3d7841db51b7e433001139918 \ + --hash=sha256:bb3ebc86782049f6928dcc583008287cb1c17d463501c94a620f035f5b4fd463 \ + --hash=sha256:bd835cdb37a1adec359dd072c24f8bb14809e2644fde86fab4ee2fc9719b9483 \ + --hash=sha256:c2292144505fb12156b981bd440f3dc994a883da06ac726c0c8692ccdbc1c510 \ + --hash=sha256:c4979e7e8887862bbb44d203f00cc8263a3f27237876fa691b6beba23e40e6d8 \ + --hash=sha256:c5c8e180cb2cabe37300e1e36c60aa4f2ff956cc579f0142135a5d2cba252243 \ + --hash=sha256:c797f8a80957134f6dd9690367a0f8f5906d672119af2c6aa55f0c527b656bed \ + --hash=sha256:c9786665926a09630c5d420c79762cfadbff35a9438bcbc4c81a9fb5ab9228b7 \ + --hash=sha256:cee36b3c0d0354f880fa7a7fdcdaf2bb5e542c2281e25c1bfadf8cfe21eba7d2 \ + --hash=sha256:d53ffa94b2340dbf6b055e09a0090618c60482c158ecfc9565642fc996bf0944 \ + --hash=sha256:df4a644af9ae132d4bfdb2e9516ea51a615fd881caddfbfbd071cf1354844479 \ + --hash=sha256:dff3de1294fbbc1db0ba6b511f77b8e540601d092538a31312e99c8a91a78b1e \ + --hash=sha256:e46767f28dea610e02edf6c5d956ce615c3c7790ea396660b9b1efd5c5ead2e0 \ + --hash=sha256:e4fab10f8403169ce92f3cea921609d9ee81107306caae06c08f592d4b8ad2b5 \ + --hash=sha256:e537e95514dae1aaa718f481ec03151a0f0394bcd05f1322896d8fc1330cb729 \ + --hash=sha256:e68c76b84e0c132d9dbf9307f12ff8185702328187a87b9aca8c941303873433 \ + --hash=sha256:e816db649ba5d7de0568cf3a9f287a9dc9aad21cf0ca667ab156a7ef47fca0b0 \ + --hash=sha256:f09645e0ce4e3825fa0baa8254064a716ed0be33f78feeedd4731016cb8aaa17 \ + --hash=sha256:f3ee3d241ed77a4fc99ce3cff3b289c3ebce37f61fdd7349d3592c23b82c8784 \ + --hash=sha256:faf03e4c2aafd6de626dbd30ba246d369ae33f47f10629d1bbe40f72115027a6 \ + --hash=sha256:ff5aa3f1c7e3f08eb0e7a016c91ba468b1850ccfd63d9b1f12f56350f4974cef + # via feast (pyproject.toml) pymssql==2.3.2 \ --hash=sha256:06883bc9bdb297ae9132d9371b5b1a3a223c8f93dd6a87d1c112c6a688f26d53 \ --hash=sha256:0768d90f96ae3267d7561d3bcfe94dd671d107489e870388b12570c3debbc552 \ @@ -4328,9 +4406,9 @@ pyodbc==5.3.0 \ # via # feast (pyproject.toml) # ibis-framework -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python pyparsing==3.3.2 \ --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ @@ -4556,9 +4634,9 @@ python-keycloak==4.2.2 \ --hash=sha256:1d43a1accd4a038ed39317fcb3eb78211df6c75bbcbc4c482c99ee76327136f2 \ --hash=sha256:5137fd87c69031a372a578df96bae96b9aead2c9dad976613bc978e9e0246a1e # via feast (pyproject.toml) -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp python-pptx==1.0.2 \ --hash=sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba \ @@ -4794,121 +4872,121 @@ referencing==0.37.0 \ # jsonschema # jsonschema-specifications # jupyter-events -regex==2026.3.32 \ - --hash=sha256:03c2ebd15ff51e7b13bb3dc28dd5ac18cd39e59ebb40430b14ae1a19e833cff1 \ - --hash=sha256:09e26cad1544d856da85881ad292797289e4406338afe98163f3db9f7fac816c \ - --hash=sha256:0cec365d44835b043d7b3266487797639d07d621bec9dc0ea224b00775797cc1 \ - --hash=sha256:0d7855f5e59fcf91d0c9f4a51dc5d8847813832a2230c3e8e35912ccf20baaa2 \ - --hash=sha256:0f21ae18dfd15752cdd98d03cbd7a3640be826bfd58482a93f730dbd24d7b9fb \ - --hash=sha256:10fb2aaae1aaadf7d43c9f3c2450404253697bf8b9ce360bd5418d1d16292298 \ - --hash=sha256:110ba4920721374d16c4c8ea7ce27b09546d43e16aea1d7f43681b5b8f80ba61 \ - --hash=sha256:12917c6c6813ffcdfb11680a04e4d63c5532b88cf089f844721c5f41f41a63ad \ - --hash=sha256:18eb45f711e942c27dbed4109830bd070d8d618e008d0db39705f3f57070a4c6 \ - --hash=sha256:1a6ac1ed758902e664e0d95c1ee5991aa6fb355423f378ed184c6ec47a1ec0e9 \ - --hash=sha256:1ca02ff0ef33e9d8276a1fcd6d90ff6ea055a32c9149c0050b5b67e26c6d2c51 \ - --hash=sha256:1cb22fa9ee6a0acb22fc9aecce5f9995fe4d2426ed849357d499d62608fbd7f9 \ - --hash=sha256:1e0f6648fd48f4c73d801c55ab976cd602e2da87de99c07bff005b131f269c6a \ - --hash=sha256:245667ad430745bae6a1e41081872d25819d86fbd9e0eec485ba00d9f78ad43d \ - --hash=sha256:2820d2231885e97aff0fcf230a19ebd5d2b5b8a1ba338c20deb34f16db1c7897 \ - --hash=sha256:2c8d402ea3dfe674288fe3962016affd33b5b27213d2b5db1823ffa4de524c57 \ - --hash=sha256:2dcca2bceb823c9cc610e57b86a265d7ffc30e9fe98548c609eba8bd3c0c2488 \ - --hash=sha256:2ffbadc647325dd4e3118269bda93ded1eb5f5b0c3b7ba79a3da9fbd04f248e9 \ - --hash=sha256:34c905a721ddee0f84c99e3e3b59dd4a5564a6fe338222bc89dd4d4df166115c \ - --hash=sha256:3c054e39a9f85a3d76c62a1d50c626c5e9306964eaa675c53f61ff7ec1204bbb \ - --hash=sha256:3c0bbfbd38506e1ea96a85da6782577f06239cb9fcf9696f1ea537c980c0680b \ - --hash=sha256:3e221b615f83b15887636fcb90ed21f1a19541366f8b7ba14ba1ad8304f4ded4 \ - --hash=sha256:3ea568832eca219c2be1721afa073c1c9eb8f98a9733fdedd0a9747639fc22a5 \ - --hash=sha256:3f5747501b69299c6b0b047853771e4ed390510bada68cb16da9c9c2078343f7 \ - --hash=sha256:462a041d2160090553572f6bb0be417ab9bb912a08de54cb692829c871ee88c1 \ - --hash=sha256:4bc32b4dbdb4f9f300cf9f38f8ea2ce9511a068ffaa45ac1373ee7a943f1d810 \ - --hash=sha256:4d082be64e51671dd5ee1c208c92da2ddda0f2f20d8ef387e57634f7e97b6aae \ - --hash=sha256:4f9ae4755fa90f1dc2d0d393d572ebc134c0fe30fcfc0ab7e67c1db15f192041 \ - --hash=sha256:51a93452034d671b0e21b883d48ea66c5d6a05620ee16a9d3f229e828568f3f0 \ - --hash=sha256:51fb7e26f91f9091fd8ec6a946f99b15d3bc3667cb5ddc73dd6cb2222dd4a1cc \ - --hash=sha256:5336b1506142eb0f23c96fb4a34b37c4fefd4fed2a7042069f3c8058efe17855 \ - --hash=sha256:567b57eb987547a23306444e4f6f85d4314f83e65c71d320d898aa7550550443 \ - --hash=sha256:5aa78c857c1731bdd9863923ffadc816d823edf475c7db6d230c28b53b7bdb5e \ - --hash=sha256:5bf2f3c2c5bd8360d335c7dcd4a9006cf1dabae063ee2558ee1b07bbc8a20d88 \ - --hash=sha256:5c35d097f509cf7e40d20d5bee548d35d6049b36eb9965e8d43e4659923405b9 \ - --hash=sha256:5d86e3fb08c94f084a625c8dc2132a79a3a111c8bf6e2bc59351fa61753c2f6e \ - --hash=sha256:6062c4ef581a3e9e503dccf4e1b7f2d33fdc1c13ad510b287741ac73bc4c6b27 \ - --hash=sha256:6128dd0793a87287ea1d8bf16b4250dd96316c464ee15953d5b98875a284d41e \ - --hash=sha256:631f7d95c83f42bccfe18946a38ad27ff6b6717fb4807e60cf24860b5eb277fc \ - --hash=sha256:66a5083c3ffe5a5a95f8281ea47a88072d4f24001d562d1d9d28d4cdc005fec5 \ - --hash=sha256:66d3126afe7eac41759cd5f0b3b246598086e88e70527c0d68c9e615b81771c4 \ - --hash=sha256:67015a8162d413af9e3309d9a24e385816666fbf09e48e3ec43342c8536f7df6 \ - --hash=sha256:6980ceb5c1049d4878632f08ba0bf7234c30e741b0dc9081da0f86eca13189d3 \ - --hash=sha256:69a847a6ffaa86e8af7b9e7037606e05a6f663deec516ad851e8e05d9908d16a \ - --hash=sha256:6ada7bd5bb6511d12177a7b00416ce55caee49fbf8c268f26b909497b534cacb \ - --hash=sha256:70c634e39c5cda0da05c93d6747fdc957599f7743543662b6dbabdd8d3ba8a96 \ - --hash=sha256:7cdd508664430dd51b8888deb6c5b416d8de046b2e11837254378d31febe4a98 \ - --hash=sha256:844d88509c968dd44b30daeefac72b038b1bf31ac372d5106358ab01d393c48b \ - --hash=sha256:847087abe98b3c1ebf1eb49d6ef320dbba75a83ee4f83c94704580f1df007dd4 \ - --hash=sha256:85c9b0c131427470a6423baa0a9330be6fd8c3630cc3ee6fdee03360724cbec5 \ - --hash=sha256:879ae91f2928a13f01a55cfa168acedd2b02b11b4cd8b5bb9223e8cde777ca52 \ - --hash=sha256:887a9fa74418d74d645281ee0edcf60694053bd1bc2ebc49eb5e66bfffc6d107 \ - --hash=sha256:88ebc0783907468f17fca3d7821b30f9c21865a721144eb498cb0ff99a67bcac \ - --hash=sha256:89e50667e7e8c0e7903e4d644a2764fffe9a3a5d6578f72ab7a7b4205bf204b7 \ - --hash=sha256:8a4a3189a99ecdd1c13f42513ab3fc7fa8311b38ba7596dd98537acb8cd9acc3 \ - --hash=sha256:8aaf8ee8f34b677f90742ca089b9c83d64bdc410528767273c816a863ed57327 \ - --hash=sha256:8e4c8fa46aad1a11ae2f8fcd1c90b9d55e18925829ac0d98c5bb107f93351745 \ - --hash=sha256:8fc918cd003ba0d066bf0003deb05a259baaaab4dc9bd4f1207bbbe64224857a \ - --hash=sha256:8fe14e24124ef41220e5992a0f09432f890037df6f93fd3d6b7a0feff2db16b2 \ - --hash=sha256:918db4e34a7ef3d0beee913fa54b34231cc3424676f1c19bdb85f01828d3cd37 \ - --hash=sha256:987cdfcfb97a249abc3601ad53c7de5c370529f1981e4c8c46793e4a1e1bfe8e \ - --hash=sha256:9b9118a78e031a2e4709cd2fcc3028432e89b718db70073a8da574c249b5b249 \ - --hash=sha256:9cf7036dfa2370ccc8651521fcbb40391974841119e9982fa312b552929e6c85 \ - --hash=sha256:a094e9dcafedfb9d333db5cf880304946683f43a6582bb86688f123335122929 \ - --hash=sha256:a416ee898ecbc5d8b283223b4cf4d560f93244f6f7615c1bd67359744b00c166 \ - --hash=sha256:a5d88fa37ba5e8a80ca8d956b9ea03805cfa460223ac94b7d4854ee5e30f3173 \ - --hash=sha256:ace48c5e157c1e58b7de633c5e257285ce85e567ac500c833349c363b3df69d4 \ - --hash=sha256:ad5c53f2e8fcae9144009435ebe3d9832003508cf8935c04542a1b3b8deefa15 \ - --hash=sha256:ad8d372587e659940568afd009afeb72be939c769c552c9b28773d0337251391 \ - --hash=sha256:b193ed199848aa96618cd5959c1582a0bf23cd698b0b900cb0ffe81b02c8659c \ - --hash=sha256:b2e9c2ea2e93223579308263f359eab8837dc340530b860cb59b713651889f14 \ - --hash=sha256:b3aa21bad31db904e0b9055e12c8282df62d43169c4a9d2929407060066ebc74 \ - --hash=sha256:b565f25171e04d4fad950d1fa837133e3af6ea6f509d96166eed745eb0cf63bc \ - --hash=sha256:b56993a7aeb4140c4770f4f7965c9e5af4f024457d06e23c01b0d47501cb18ed \ - --hash=sha256:b6acb765e7c1f2fa08ac9057a33595e26104d7d67046becae184a8f100932dd9 \ - --hash=sha256:b6f366a5ef66a2df4d9e68035cfe9f0eb8473cdfb922c37fac1d169b468607b0 \ - --hash=sha256:b7836aa13721dbdef658aebd11f60d00de633a95726521860fe1f6be75fa225a \ - --hash=sha256:b8fca73e16c49dd972ce3a88278dfa5b93bf91ddef332a46e9443abe21ca2f7c \ - --hash=sha256:b953d9d496d19786f4d46e6ba4b386c6e493e81e40f9c5392332458183b0599d \ - --hash=sha256:bbc458a292aee57d572075f22c035fa32969cdb7987d454e3e34d45a40a0a8b4 \ - --hash=sha256:c1cecea3e477af105f32ef2119b8d895f297492e41d317e60d474bc4bffd62ff \ - --hash=sha256:c1d7fa44aece1fa02b8927441614c96520253a5cad6a96994e3a81e060feed55 \ - --hash=sha256:c1ed17104d1be7f807fdec35ec99777168dd793a09510d753f8710590ba54cdd \ - --hash=sha256:c3c6f6b027d10f84bfe65049028892b5740878edd9eae5fea0d1710b09b1d257 \ - --hash=sha256:c5e0fdb5744caf1036dec5510f543164f2144cb64932251f6dfd42fa872b7f9c \ - --hash=sha256:c60f1de066eb5a0fd8ee5974de4194bb1c2e7692941458807162ffbc39887303 \ - --hash=sha256:c6d9c6e783b348f719b6118bb3f187b2e138e3112576c9679eb458cc8b2e164b \ - --hash=sha256:c940e00e8d3d10932c929d4b8657c2ea47d2560f31874c3e174c0d3488e8b865 \ - --hash=sha256:c9f261ad3cd97257dc1d9355bfbaa7dd703e06574bffa0fa8fe1e31da915ee38 \ - --hash=sha256:d21a07edddb3e0ca12a8b8712abc8452481c3d3db19ae87fc94e9842d005964b \ - --hash=sha256:d363660f9ef8c734495598d2f3e527fb41f745c73159dc0d743402f049fb6836 \ - --hash=sha256:d478a2ca902b6ef28ffc9521e5f0f728d036abe35c0b250ee8ae78cfe7c5e44e \ - --hash=sha256:d571f0b2eec3513734ea31a16ce0f7840c0b85a98e7edfa0e328ed144f9ef78f \ - --hash=sha256:d6b39a2cc5625bbc4fda18919a891eab9aab934eecf83660a90ce20c53621a9a \ - --hash=sha256:d76d62909bfb14521c3f7cfd5b94c0c75ec94b0a11f647d2f604998962ec7b6c \ - --hash=sha256:dab4178a0bc1ef13178832b12db7bc7f562e8f028b2b5be186e370090dc50652 \ - --hash=sha256:db976be51375bca900e008941639448d148c655c9545071965d0571ecc04f5d0 \ - --hash=sha256:ded4fc0edf3de792850cb8b04bbf3c5bd725eeaf9df4c27aad510f6eed9c4e19 \ - --hash=sha256:e006ea703d5c0f3d112b51ba18af73b58209b954acfe3d8da42eacc9a00e4be6 \ - --hash=sha256:e3e5d1802cba785210a4a800e63fcee7a228649a880f3bf7f2aadccb151a834b \ - --hash=sha256:e480d3dac06c89bc2e0fd87524cc38c546ac8b4a38177650745e64acbbcfdeba \ - --hash=sha256:e50af656c15e2723eeb7279c0837e07accc594b95ec18b86821a4d44b51b24bf \ - --hash=sha256:e83ce8008b48762be296f1401f19afd9ea29f3d035d1974e0cecb74e9afbd1df \ - --hash=sha256:ed3b8281c5d0944d939c82db4ec2300409dd69ee087f7a75a94f2e301e855fb4 \ - --hash=sha256:ef250a3f5e93182193f5c927c5e9575b2cb14b80d03e258bc0b89cc5de076b60 \ - --hash=sha256:f1574566457161678297a116fa5d1556c5a4159d64c5ff7c760e7c564bf66f16 \ - --hash=sha256:f26262900edd16272b6360014495e8d68379c6c6e95983f9b7b322dc928a1194 \ - --hash=sha256:f28eac18a8733a124444643a66ac96fef2c0ad65f50034e0a043b90333dc677f \ - --hash=sha256:f54840bea73541652f1170dc63402a5b776fc851ad36a842da9e5163c1f504a0 \ - --hash=sha256:f785f44a44702dea89b28bce5bc82552490694ce4e144e21a4f0545e364d2150 \ - --hash=sha256:f7cc00089b4c21847852c0ad76fb3680f9833b855a0d30bcec94211c435bff6b \ - --hash=sha256:f95bd07f301135771559101c060f558e2cf896c7df00bec050ca7f93bf11585a \ - --hash=sha256:fc8ced733d6cd9af5e412f256a32f7c61cd2d7371280a65c689939ac4572499f \ - --hash=sha256:fd03e38068faeef937cc6761a250a4aaa015564bd0d61481fefcf15586d31825 +regex==2026.4.4 \ + --hash=sha256:011bb48bffc1b46553ac704c975b3348717f4e4aa7a67522b51906f99da1820c \ + --hash=sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f \ + --hash=sha256:0540e5b733618a2f84e9cb3e812c8afa82e151ca8e19cf6c4e95c5a65198236f \ + --hash=sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62 \ + --hash=sha256:0709f22a56798457ae317bcce42aacee33c680068a8f14097430d9f9ba364bee \ + --hash=sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883 \ + --hash=sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13 \ + --hash=sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99 \ + --hash=sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a \ + --hash=sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0 \ + --hash=sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566 \ + --hash=sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9 \ + --hash=sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76 \ + --hash=sha256:1b9a00b83f3a40e09859c78920571dcb83293c8004079653dd22ec14bbfa98c7 \ + --hash=sha256:21e5eb86179b4c67b5759d452ea7c48eb135cd93308e7a260aa489ed2eb423a4 \ + --hash=sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717 \ + --hash=sha256:2895506ebe32cc63eeed8f80e6eae453171cfccccab35b70dc3129abec35a5b8 \ + --hash=sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17 \ + --hash=sha256:2a5d273181b560ef8397c8825f2b9d57013de744da9e8257b8467e5da8599351 \ + --hash=sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d \ + --hash=sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb \ + --hash=sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7 \ + --hash=sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8 \ + --hash=sha256:312ec9dd1ae7d96abd8c5a36a552b2139931914407d26fba723f9e53c8186f86 \ + --hash=sha256:33424f5188a7db12958246a54f59a435b6cb62c5cf9c8d71f7cc49475a5fdada \ + --hash=sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81 \ + --hash=sha256:33bfda9684646d323414df7abe5692c61d297dbb0530b28ec66442e768813c59 \ + --hash=sha256:349d7310eddff40429a099c08d995c6d4a4bfaf3ff40bd3b5e5cb5a5a3c7d453 \ + --hash=sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141 \ + --hash=sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031 \ + --hash=sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74 \ + --hash=sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244 \ + --hash=sha256:415a994b536440f5011aa77e50a4274d15da3245e876e5c7f19da349caaedd87 \ + --hash=sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f \ + --hash=sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465 \ + --hash=sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983 \ + --hash=sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff \ + --hash=sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0 \ + --hash=sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55 \ + --hash=sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752 \ + --hash=sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73 \ + --hash=sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe \ + --hash=sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95 \ + --hash=sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8 \ + --hash=sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb \ + --hash=sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45 \ + --hash=sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943 \ + --hash=sha256:6780f008ee81381c737634e75c24e5a6569cc883c4f8e37a37917ee79efcafd9 \ + --hash=sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520 \ + --hash=sha256:6aa809ed4dc3706cc38594d67e641601bd2f36d5555b2780ff074edfcb136cf8 \ + --hash=sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1 \ + --hash=sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3 \ + --hash=sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1 \ + --hash=sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb \ + --hash=sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6 \ + --hash=sha256:74fa82dcc8143386c7c0392e18032009d1db715c25f4ba22d23dc2e04d02a20f \ + --hash=sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be \ + --hash=sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4 \ + --hash=sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951 \ + --hash=sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27 \ + --hash=sha256:7d346fccdde28abba117cc9edc696b9518c3307fbfcb689e549d9b5979018c6d \ + --hash=sha256:8512fcdb43f1bf18582698a478b5ab73f9c1667a5b7548761329ef410cd0a760 \ + --hash=sha256:867bddc63109a0276f5a31999e4c8e0eb7bbbad7d6166e28d969a2c1afeb97f9 \ + --hash=sha256:88e9b048345c613f253bea4645b2fe7e579782b82cac99b1daad81e29cc2ed8e \ + --hash=sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7 \ + --hash=sha256:9542ccc1e689e752594309444081582f7be2fdb2df75acafea8a075108566735 \ + --hash=sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81 \ + --hash=sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3 \ + --hash=sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9 \ + --hash=sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790 \ + --hash=sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043 \ + --hash=sha256:a0d2b28aa1354c7cd7f71b7658c4326f7facac106edd7f40eda984424229fd59 \ + --hash=sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a \ + --hash=sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4 \ + --hash=sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f \ + --hash=sha256:a85b620a388d6c9caa12189233109e236b3da3deffe4ff11b84ae84e218a274f \ + --hash=sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427 \ + --hash=sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae \ + --hash=sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa \ + --hash=sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d \ + --hash=sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0 \ + --hash=sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc \ + --hash=sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863 \ + --hash=sha256:b4c36a85b00fadb85db9d9e90144af0a980e1a3d2ef9cd0f8a5bef88054657c6 \ + --hash=sha256:b5f9fb784824a042be3455b53d0b112655686fdb7a91f88f095f3fee1e2a2a54 \ + --hash=sha256:be061028481186ba62a0f4c5f1cc1e3d5ab8bce70c89236ebe01023883bc903b \ + --hash=sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52 \ + --hash=sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07 \ + --hash=sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b \ + --hash=sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b \ + --hash=sha256:cf9b1b2e692d4877880388934ac746c99552ce6bf40792a767fd42c8c99f136d \ + --hash=sha256:d2228c02b368d69b724c36e96d3d1da721561fb9cc7faa373d7bf65e07d75cb5 \ + --hash=sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf \ + --hash=sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b \ + --hash=sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359 \ + --hash=sha256:dcb5453ecf9cd58b562967badd1edbf092b0588a3af9e32ee3d05c985077ce87 \ + --hash=sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca \ + --hash=sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa \ + --hash=sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423 \ + --hash=sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4 \ + --hash=sha256:e355be718caf838aa089870259cf1776dc2a4aa980514af9d02c59544d9a8b22 \ + --hash=sha256:e7ab63e9fe45a9ec3417509e18116b367e89c9ceb6219222a3396fa30b147f80 \ + --hash=sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f \ + --hash=sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17 \ + --hash=sha256:eb59c65069498dbae3c0ef07bbe224e1eaa079825a437fb47a479f0af11f774f \ + --hash=sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e \ + --hash=sha256:ee9627de8587c1a22201cb16d0296ab92b4df5cdcb5349f4e9744d61db7c7c98 \ + --hash=sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4 \ + --hash=sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d \ + --hash=sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b \ + --hash=sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c \ + --hash=sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83 \ + --hash=sha256:fe896e07a5a2462308297e515c0054e9ec2dd18dfdc9427b19900b37dfe6f40b \ + --hash=sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e # via # feast (pyproject.toml) # parsimonious @@ -4974,9 +5052,9 @@ rfc3987-syntax==1.1.0 \ --hash=sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f \ --hash=sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d # via jsonschema -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==14.3.4 \ + --hash=sha256:07e7adb4690f68864777b1450859253bed81a99a31ac321ac1817b2313558952 \ + --hash=sha256:817e02727f2b25b40ef56f5aa2217f400c8489f79ca8f46ea2b70dd5e14558a9 # via # codeflare-sdk # fastapi-mcp @@ -5118,25 +5196,25 @@ ruamel-yaml==0.17.17 \ --hash=sha256:9751de4cbb57d4bfbf8fc394e125ed4a2f170fbff3dc3d78abf50be85924f8be \ --hash=sha256:9af3ec5d7f8065582f3aa841305465025d0afd26c5fb54e15b964e11838fc74f # via great-expectations -ruff==0.15.9 \ - --hash=sha256:058d8e99e1bfe79d8a0def0b481c56059ee6716214f7e425d8e737e412d69677 \ - --hash=sha256:0694e601c028fd97dc5c6ee244675bc241aeefced7ef80cd9c6935a871078f53 \ - --hash=sha256:29cbb1255a9797903f6dde5ba0188c707907ff44a9006eb273b5a17bfa0739a2 \ - --hash=sha256:2b0c7c341f68adb01c488c3b7d4b49aa8ea97409eae6462d860a79cf55f431b6 \ - --hash=sha256:45a70921b80e1c10cf0b734ef09421f71b5aa11d27404edc89d7e8a69505e43d \ - --hash=sha256:4965bac6ac9ea86772f4e23587746f0b7a395eccabb823eb8bfacc3fa06069f7 \ - --hash=sha256:55cc15eee27dc0eebdfcb0d185a6153420efbedc15eb1d38fe5e685657b0f840 \ - --hash=sha256:6d3fcbca7388b066139c523bda744c822258ebdcfbba7d24410c3f454cc9af71 \ - --hash=sha256:6efbe303983441c51975c243e26dff328aca11f94b70992f35b093c2e71801e1 \ - --hash=sha256:7b34a9766aeec27a222373d0b055722900fbc0582b24f39661aa96f3fe6ad901 \ - --hash=sha256:89dd695bc72ae76ff484ae54b7e8b0f6b50f49046e198355e44ea656e521fef9 \ - --hash=sha256:8e1ddb11dbd61d5983fa2d7d6370ef3eb210951e443cace19594c01c72abab4c \ - --hash=sha256:9439a342adb8725f32f92732e2bafb6d5246bd7a5021101166b223d312e8fc59 \ - --hash=sha256:9c5e6faf9d97c8edc43877c3f406f47446fc48c40e1442d58cfcdaba2acea745 \ - --hash=sha256:a6537f6eed5cda688c81073d46ffdfb962a5f29ecb6f7e770b2dc920598997ed \ - --hash=sha256:bde6ff36eaf72b700f32b7196088970bf8fdb2b917b7accd8c371bfc0fd573ec \ - --hash=sha256:ce187224ef1de1bd225bc9a152ac7102a6171107f026e81f317e4257052916d5 \ - --hash=sha256:eaf05aad70ca5b5a0a4b0e080df3a6b699803916d88f006efd1f5b46302daab8 +ruff==0.15.12 \ + --hash=sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b \ + --hash=sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33 \ + --hash=sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0 \ + --hash=sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002 \ + --hash=sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339 \ + --hash=sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e \ + --hash=sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847 \ + --hash=sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f \ + --hash=sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6 \ + --hash=sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d \ + --hash=sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20 \ + --hash=sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd \ + --hash=sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c \ + --hash=sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5 \ + --hash=sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6 \ + --hash=sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c \ + --hash=sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5 \ + --hash=sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5 # via feast (pyproject.toml) s3transfer==0.13.1 \ --hash=sha256:a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724 \ @@ -5340,9 +5418,9 @@ send2trash==2.1.0 \ --hash=sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c \ --hash=sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459 # via jupyter-server -sentence-transformers==5.3.0 \ - --hash=sha256:414a0a881f53a4df0e6cbace75f823bfcb6b94d674c42a384b498959b7c065e2 \ - --hash=sha256:dca6b98db790274a68185d27a65801b58b4caf653a4e556b5f62827509347c7d +sentence-transformers==5.4.1 \ + --hash=sha256:436bcb1182a0ff42a8fb2b1c43498a70d0a75b688d182f2cd0d1dd115af61ddc \ + --hash=sha256:a6d640fc363849b63affb8e140e9d328feabab86f83d58ac3e16b1c28140b790 # via feast (pyproject.toml) setuptools==80.10.2 \ --hash=sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70 \ @@ -5442,9 +5520,9 @@ six==1.17.0 \ # python-dateutil # rfc3339-validator # thriftpy2 -smart-open==7.5.1 \ - --hash=sha256:3e07cbbd9c8a908bcb8e25d48becf1a5cbb4886fa975e9f34c672ed171df2318 \ - --hash=sha256:3f08e16827c4733699e6b2cc40328a3568f900cb12ad9a3ad233ba6c872d9fe7 +smart-open==7.6.0 \ + --hash=sha256:2a78f454610a826aa688065b54b4a0a9b12a5599fa61d5190e9bac2df5e5f53f \ + --hash=sha256:44717f46b5ff276fac03b88e5d13d1c416f064f3b7b081381b0fa8889004bd7e # via ray sniffio==1.3.1 \ --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ @@ -5520,103 +5598,103 @@ sphinxcontrib-serializinghtml==2.0.0 \ --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \ --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d # via sphinx -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot[rs]==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot[rs]==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via # feast (pyproject.toml) # ibis-framework -sqlglotc==30.2.1 \ - --hash=sha256:052cd7bb41fc9b841eb268d4dd601eb6b5954b7c6d5656795d4350a0f8020d53 \ - --hash=sha256:058f0e9aed2b8dff87dc893b8793e514204c8dfef699b7d3d1704dfbdd949f2b \ - --hash=sha256:0e6be524252894c0fa98d25d4e60dfae6485ba66ca1abd40bf05f16a9cf26baf \ - --hash=sha256:13f8f68808777ba7d845bc908bf09f72a0c9899a19811483dc52f0fa48b38d5a \ - --hash=sha256:1a004086ab871be0cc97766f7b6fb8866729f09dd7272254fd31c05107f3fdc8 \ - --hash=sha256:25c6f62f31cd3a051285635c3f6a01d2f3c73ca2baaa26970815166928042ace \ - --hash=sha256:2b5fe8adc1a1e2fb819e014e94974a274f30dbf9684ceed9f171fb0889f80f0b \ - --hash=sha256:2ffe527bc8664b03cc936bae7ebf965f482beb4acee7a815c2ec2d9aea720b4e \ - --hash=sha256:4aa90e08f53409b1857572836e57a31835ed20e32521c6fafdc6af96199baff7 \ - --hash=sha256:507935a971e0a9e5d4ac7ca14df479f8e270502b44904f71d95c0aaed066006f \ - --hash=sha256:515e092ab8fb522b256fa8a34f471e9b187bb8a50a7c0226a65b036a07d6d188 \ - --hash=sha256:585bb610fde3e3dd1d7e5ff3cce14f70fbd53ced6769cd104679adf8b5c4ab5b \ - --hash=sha256:850e7517dd4739cad9af65bcb9699825f9202e5971407bf955e3248fe4814f96 \ - --hash=sha256:8f063af733cbcc51686380470e7f3f80b589b8c58084baa138efb3b8ca821597 \ - --hash=sha256:b17e3002ed10747388367621b2ecf39c06d5fdc6b3c31a8c32be2f5ef546fc0b \ - --hash=sha256:d577e1635e127febb7012bc42fa1c3b958076e59a1a116ade20048c572a1be42 \ - --hash=sha256:dc292cd73e0c447253877c27f00454a2d09b71324a130ad4c58c145ab753889e \ - --hash=sha256:de168df756a21a028cf1f917f92da2f77bb135f3b6cdd960914460942a5eca10 \ - --hash=sha256:de884dd224220002c3e940ca5bdceb27ef9638e5f02493db133ffb8ae88b5610 \ - --hash=sha256:f33c7d1646ff6531cb9b07f0740b2939f3ecaa31efebfbec8adb6b275f1a45f2 \ - --hash=sha256:f9a1fc7b1ff3b51d0d03a391768a79964f68541b4c2f294a25a6f14e6670ffab \ - --hash=sha256:fae4edad0b7c5f9f963bd63452f722f0d7f77a436c2d334b555b31722f9573ad \ - --hash=sha256:fdc19623a1c7659918c3cee18ea8849fc4af9eaeb87247acf37e0393295d32b7 \ - --hash=sha256:feefc0ab7606d1fe284d23bef09ea4829ce4fad679936959c29324310f23e081 \ - --hash=sha256:ff19b7ecb931aef6c7c6168af5530c07e67915102b701d45ae80446f0695ba54 +sqlglotc==30.6.0 \ + --hash=sha256:003b15bbe3f3a4a63313baeb3f090906cf0172fe007ab24974c612a577f56c61 \ + --hash=sha256:05861b5d74ae4b0a5a6e6a9309a8a975d9572c2bf1eb9634a33af9189eb7d333 \ + --hash=sha256:0b907206ef36fd8f0c28da4c5b8c8f896bd67826da0d765616e6c950689ac849 \ + --hash=sha256:0e1aae195008c1d87ed1b1543bceed1408ae8ecf146571b3029e98b23a19c59c \ + --hash=sha256:16291ee3da0276df2689cc139df41d8723cf6014aca979f307fb75a898b0d3e8 \ + --hash=sha256:27d6c22375395f1fdfe8a5d80b5fb781ca7849e29c7db7dfb11edb466a5b7a4d \ + --hash=sha256:2b50aba396d4622c201a9dd933b51cd6858b5af5ebbcf7db1af35db7e83ece48 \ + --hash=sha256:4a5beba24625bc14070992fdddc7aed22df912007882f31f27b0e88d7b7d9445 \ + --hash=sha256:4c84a933816374b6167d9347e488fb4a357bb0e3be2e8e820dafeb3c9948feab \ + --hash=sha256:4d45ee83e1f72ee94045ccfd13e51fa7d822548f50e9d20d3d42e127bcd9f453 \ + --hash=sha256:4eb6f349c21d5a3e39733db5416e57ec171ca3b1c17b02badfdbe55d9a3666a5 \ + --hash=sha256:61a1e4f533955db0bfd4219883bfab1f2030753ffaebc9cbb4e950dfbcae3db7 \ + --hash=sha256:63b02231cc2f10d63df373fa02f3e03b4216e94c6f32ccdfd11adc770dc95fb7 \ + --hash=sha256:6e8dab3b9f84c9b591ecd1fb0920b800c08dd6c38eda3be91176fc239d3c94bc \ + --hash=sha256:7acdb7b15f060bacea01e1c68d1dd70471f03bda89478ca0ee96bac3df03d0a7 \ + --hash=sha256:87437d4f32c1dc61c3ef046dc39b9fe119ac2e2e5253e33351f1a62ba802942a \ + --hash=sha256:875c6925f3d70aa1bc4bcc405090a12deec01de71193e7229f114c5ecf39f725 \ + --hash=sha256:949f5d457c8c98998ed06d060873370d7553077ab3c2db9569acda5887cd6ce7 \ + --hash=sha256:c69c9dbe4dfab74329294d07b2956ca9a03123089a1923f3d299a21d4b66898d \ + --hash=sha256:cb4b5a532fb35ce415aea6d360fe202278b46cc59c07318b97401930a1584e35 \ + --hash=sha256:cfba6244f52bcdffe22a35334d48d741aed3077c20bb7aa9bb511c21fd766438 \ + --hash=sha256:dd781790c3fc6cb82e6c00836f7f9ab33941cf2b9dca173d7b9f41c37c78d114 \ + --hash=sha256:e397befde3d08d870a8f1c7bd80d7abc68ee119c472143ab196709319e205af4 \ + --hash=sha256:f0a37ad865b106005cb5d36efd537a412912477c83cbca1579ee45f17d73fe54 \ + --hash=sha256:fb977d427a196a620aa60b71d4b66d60c3d015c9004e7393162d4771fb435406 # via sqlglot sqlglotrs==0.13.0 \ --hash=sha256:6b934a244b16f26fca50974328a2ebc7689583c59f06203cebb46e2e6e8d93a7 \ @@ -6016,15 +6094,16 @@ tree-sitter==0.25.2 \ --hash=sha256:fbb1706407c0e451c4f8cc016fec27d72d4b211fdd3173320b1ada7a6c74c3ac \ --hash=sha256:fe43c158555da46723b28b52e058ad444195afd1db3ca7720c59a254544e9c20 # via docling-core -tree-sitter-c==0.24.1 \ - --hash=sha256:290bff0f9c79c966496ebae45042f77543e6e4aea725f40587a8611d566231a8 \ - --hash=sha256:789781afcb710df34144f7e2a20cd80e325114b9119e3956c6bd1dd2d365df98 \ - --hash=sha256:7d2d0cda0b8dda428c81440c1e94367f9f13548eedca3f49768bde66b1422ad6 \ - --hash=sha256:942bcd7cbecd810dcf7ca6f8f834391ebf0771a89479646d891ba4ca2fdfdc88 \ - --hash=sha256:9a74cfd7a11ca5a961fafd4d751892ee65acae667d2818968a6f079397d8d28c \ - --hash=sha256:9c06ac26a1efdcc8b26a8a6970fbc6997c4071857359e5837d4c42892d45fe1e \ - --hash=sha256:a6a807705a3978911dc7ee26a7ad36dcfacb6adfc13c190d496660ec9bd66707 \ - --hash=sha256:d46bbda06f838c2dcb91daf767813671fd366b49ad84ff37db702129267b46e1 +tree-sitter-c==0.24.2 \ + --hash=sha256:1628584df0299b5a340aa63f8e67b6c97c91517f52fa7e7a4c557e40adb330a9 \ + --hash=sha256:4a2f4371cd816cc3153458f69062135ebb2ea5f275ddd90494e5c823d778204a \ + --hash=sha256:4d4579a8b54f0a442f903d88d3304cab77cd5c2031d4015baa4f2f8e15d6dcb7 \ + --hash=sha256:5041ef67eb68ce6bc8bb0b1f8ef3a5585ce523dae0c7eec109ab0627dd75aede \ + --hash=sha256:82842c5a5f2acd93f4de10038c33ac179c8979defc39376f990348d6289e933b \ + --hash=sha256:97bc80a224d48215d4e6e6376bf30d114f4c317b8145ff1b02afe785d4ba7bdd \ + --hash=sha256:abb549225091f7b25df2dd3a0143ece6e208f7055d8bcb4700b41ee79b9ef1e1 \ + --hash=sha256:c098bedcd5ac86ff93fa734d51d1dd86aed40fd5ed7d634c7af11380a0469969 \ + --hash=sha256:e2b42e8e22202c251f8629306f9321233542e07a6e01611b5fe83489272143eb # via docling-core tree-sitter-javascript==0.25.0 \ --hash=sha256:199d09985190852e0912da2b8d26c932159be314bc04952cf917ed0e4c633e6b \ @@ -6073,9 +6152,9 @@ typer==0.12.5 \ # docling # docling-core # fastapi-mcp -types-cffi==2.0.0.20260402 \ - --hash=sha256:47e1320c009f630c59c55c8e3d2b8c501e280babf52e92f6109cbfb0864ba367 \ - --hash=sha256:f647a400fba0a31d603479169d82ee5359db79bd1136e41dc7e6489296e3a2b2 +types-cffi==2.0.0.20260408 \ + --hash=sha256:68bd296742b4ff7c0afe3547f50bd0acc55416ecf322ffefd2b7344ef6388a42 \ + --hash=sha256:aa8b9c456ab715c079fc655929811f21f331bfb940f4a821987c581bf4e36230 # via types-pyopenssl types-protobuf==3.19.22 \ --hash=sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab \ @@ -6083,25 +6162,25 @@ types-protobuf==3.19.22 \ # via # feast (pyproject.toml) # mypy-protobuf -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) types-pyopenssl==24.1.0.20240722 \ --hash=sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39 \ --hash=sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54 # via types-redis -types-python-dateutil==2.9.0.20260402 \ - --hash=sha256:7827e6a9c93587cc18e766944254d1351a2396262e4abe1510cbbd7601c5e01f \ - --hash=sha256:a980142b9966713acb382c467e35c5cc4208a2f91b10b8d785a0ae6765df6c0b +types-python-dateutil==2.9.0.20260408 \ + --hash=sha256:473139d514a71c9d1fbd8bb328974bedcb1cc3dba57aad04ffa4157f483c216f \ + --hash=sha256:8b056ec01568674235f64ecbcef928972a5fac412f5aab09c516dfa2acfbb582 # via feast (pyproject.toml) -types-pytz==2026.1.1.20260402 \ - --hash=sha256:0d9a60ed1c6ad4fce7c6395b5bd2d9827db41d4b83de7c0322cf85869c2bfda3 \ - --hash=sha256:79209aa51dc003a4a6a764234d92b14e5c09a1b7f24e0f00c493929fd33618e8 +types-pytz==2026.1.1.20260408 \ + --hash=sha256:89b6a34b9198ea2a4b98a9d15cbca987053f52a105fd44f7ce3789cae4349408 \ + --hash=sha256:c7e4dec76221fb7d0c97b91ad8561d689bebe39b6bcb7b728387e7ffd8cde788 # via feast (pyproject.toml) -types-pyyaml==6.0.12.20250915 \ - --hash=sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3 \ - --hash=sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6 +types-pyyaml==6.0.12.20260408 \ + --hash=sha256:92a73f2b8d7f39ef392a38131f76b970f8c66e4c42b3125ae872b7c93b556307 \ + --hash=sha256:fbc42037d12159d9c801ebfcc79ebd28335a7c13b08a4cfbc6916df78fee9384 # via feast (pyproject.toml) types-redis==4.6.0.20241004 \ --hash=sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e \ @@ -6111,15 +6190,15 @@ types-requests==2.30.0.0 \ --hash=sha256:c6cf08e120ca9f0dc4fa4e32c3f953c3fba222bcc1db6b97695bce8da1ba9864 \ --hash=sha256:dec781054324a70ba64430ae9e62e7e9c8e4618c185a5cb3f87a6738251b5a31 # via feast (pyproject.toml) -types-setuptools==82.0.0.20260402 \ - --hash=sha256:4b9a9f6c3c4c65107a3956ad6a6acbccec38e398ff6d5f78d5df7f103dadb8d6 \ - --hash=sha256:63d2b10ba7958396ad79bbc24d2f6311484e452daad4637ffd40407983a27069 +types-setuptools==82.0.0.20260408 \ + --hash=sha256:036c68caf7e672a699f5ebbf914708d40644c14e05298bc49f7272be91cf43d3 \ + --hash=sha256:ece0a215cdfa6463a65fd6f68bd940f39e455729300ddfe61cab1147ed1d2462 # via # feast (pyproject.toml) # types-cffi -types-tabulate==0.10.0.20260308 \ - --hash=sha256:724dcb1330ffba5f46d3cf6e29f45089fccb8e85801e6e7ac9efb1195bf7bea1 \ - --hash=sha256:94a9795965bc6290f844d61e8680a1270040664b88fd12014624090fd847e13c +types-tabulate==0.10.0.20260408 \ + --hash=sha256:2b19d193603d38c34645de53c0c1087e2364487d518d4a2f44268db2366723cc \ + --hash=sha256:903d62fdf7e5a0ff659fd5d629df716232f7658c6d30e98f0374488d06ffacf4 # via feast (pyproject.toml) types-urllib3==1.26.25.14 \ --hash=sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f \ @@ -6175,9 +6254,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # arrow # ibis-framework @@ -6557,9 +6636,9 @@ werkzeug==3.1.8 \ --hash=sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50 \ --hash=sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44 # via moto -wheel==0.46.3 \ - --hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d \ - --hash=sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803 +wheel==0.47.0 \ + --hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \ + --hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3 # via # pip-tools # singlestoredb @@ -6651,6 +6730,7 @@ wrapt==1.17.3 \ --hash=sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c # via # aiobotocore + # deprecated # smart-open # testcontainers xlsxwriter==3.2.9 \ @@ -6933,9 +7013,9 @@ yarl==1.23.0 \ --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \ --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d # via aiohttp -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata zstandard==0.25.0 \ --hash=sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64 \ diff --git a/sdk/python/requirements/py3.11-minimal-requirements.txt b/sdk/python/requirements/py3.11-minimal-requirements.txt index 741d147c860..822bc342959 100644 --- a/sdk/python/requirements/py3.11-minimal-requirements.txt +++ b/sdk/python/requirements/py3.11-minimal-requirements.txt @@ -194,9 +194,9 @@ botocore==1.38.46 \ # boto3 # s3transfer # snowflake-connector-python -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # httpcore # httpx @@ -424,9 +424,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -440,56 +440,56 @@ colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via feast (pyproject.toml) -cryptography==46.0.6 \ - --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ - --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ - --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ - --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ - --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ - --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ - --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ - --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ - --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ - --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ - --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ - --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ - --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ - --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ - --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ - --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ - --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ - --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ - --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ - --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ - --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ - --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ - --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ - --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ - --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ - --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ - --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ - --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ - --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ - --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ - --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ - --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ - --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ - --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ - --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ - --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ - --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ - --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ - --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ - --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 +cryptography==47.0.0 \ + --hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \ + --hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \ + --hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \ + --hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \ + --hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \ + --hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \ + --hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \ + --hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \ + --hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \ + --hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \ + --hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \ + --hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \ + --hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \ + --hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \ + --hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \ + --hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \ + --hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \ + --hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \ + --hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \ + --hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \ + --hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \ + --hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \ + --hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \ + --hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \ + --hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \ + --hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \ + --hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \ + --hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \ + --hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \ + --hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \ + --hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \ + --hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \ + --hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \ + --hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \ + --hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \ + --hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \ + --hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \ + --hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \ + --hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \ + --hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \ + --hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \ + --hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \ + --hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \ + --hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \ + --hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \ + --hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \ + --hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \ + --hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \ + --hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736 # via # google-auth # pyjwt @@ -509,50 +509,50 @@ dill==0.3.9 \ --hash=sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a \ --hash=sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c # via feast (pyproject.toml) -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ --hash=sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286 # via kubernetes -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -560,9 +560,9 @@ fastapi-mcp==0.4.0 \ --hash=sha256:d4a3fe7966af24d44e4b412720561c95eb12bed999a4443a88221834b3b15aec \ --hash=sha256:d4ca9410996f4c7b8ea0d7b20fdf79878dc359ebf89cbf3b222e0b675a55097d # via feast (pyproject.toml) -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via snowflake-connector-python frozenlist==1.8.0 \ --hash=sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686 \ @@ -704,9 +704,9 @@ fsspec==2024.9.0 \ # via # feast (pyproject.toml) # dask -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -716,9 +716,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-datastore # google-cloud-storage # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -1072,9 +1072,9 @@ ibis-framework[duckdb]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1110,97 +1110,97 @@ kubernetes==35.0.0 \ --hash=sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d \ --hash=sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee # via feast (pyproject.toml) -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -1577,51 +1577,51 @@ multidict==6.7.1 \ # aiobotocore # aiohttp # yarl -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -1711,9 +1711,9 @@ oauthlib==3.3.1 \ --hash=sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9 \ --hash=sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1 # via requests-oauthlib -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # db-dtypes @@ -1787,9 +1787,9 @@ pandas==2.3.3 \ # pandas-gbq # pymilvus # snowflake-connector-python -pandas-gbq==0.34.1 \ - --hash=sha256:6bea5b85937251b976cf9db38151ea59abbff98771179183488d4614694bff67 \ - --hash=sha256:b74932c6ee35dfc81582f39c792e3a68c9ef9bee8c85f25667d9d05dfadd0daf +pandas-gbq==0.35.0 \ + --hash=sha256:258de481019566611031919997bf9c1ece4ca30a4dd02d3fc3664b251d446182 \ + --hash=sha256:596c908487ef0649a161e86ef272c00c267e581b95dc5ee0dc3518545b33bcfc # via google-cloud-bigquery parsy==2.2 \ --hash=sha256:5e981613d9d2d8b68012d1dd0afe928967bea2e4eefdb76c2f545af0dd02a9e7 \ @@ -1799,17 +1799,17 @@ partd==1.4.2 \ --hash=sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f \ --hash=sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c # via dask -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via mypy -platformdirs==4.9.4 \ - --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ - --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 +platformdirs==4.9.6 \ + --hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \ + --hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917 # via snowflake-connector-python -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) propcache==0.4.1 \ --hash=sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e \ @@ -2005,57 +2005,57 @@ psycopg-pool==3.3.0 \ --hash=sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 \ --hash=sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5 # via psycopg -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -2080,141 +2080,140 @@ pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi # fastapi-mcp # mcp # pydantic-settings -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # fastapi-mcp # mcp @@ -2243,9 +2242,9 @@ pymysql==1.1.2 \ --hash=sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03 \ --hash=sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9 # via feast (pyproject.toml) -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python python-dateutil==2.9.0.post0 \ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ @@ -2264,9 +2263,9 @@ python-dotenv==1.2.2 \ # pydantic-settings # pymilvus # uvicorn -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp pytz==2026.1.post1 \ --hash=sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1 \ @@ -2381,9 +2380,9 @@ requests-oauthlib==2.0.0 \ # via # google-auth-oauthlib # kubernetes -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 # via # fastapi-mcp # ibis-framework @@ -2560,74 +2559,74 @@ sortedcontainers==2.4.0 \ --hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \ --hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0 # via snowflake-connector-python -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via ibis-framework sse-starlette==3.3.4 \ --hash=sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1 \ @@ -2722,13 +2721,13 @@ typeguard==4.5.1 \ --hash=sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40 \ --hash=sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274 # via feast (pyproject.toml) -typer==0.24.1 \ - --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e \ - --hash=sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45 +typer==0.24.2 \ + --hash=sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8 \ + --hash=sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480 # via fastapi-mcp -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -2759,9 +2758,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # ibis-framework # pandas @@ -3306,7 +3305,7 @@ yarl==1.23.0 \ --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \ --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d # via aiohttp -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata diff --git a/sdk/python/requirements/py3.11-minimal-sdist-requirements-build.txt b/sdk/python/requirements/py3.11-minimal-sdist-requirements-build.txt index f1eaaa0c05a..252249dfd53 100644 --- a/sdk/python/requirements/py3.11-minimal-sdist-requirements-build.txt +++ b/sdk/python/requirements/py3.11-minimal-sdist-requirements-build.txt @@ -214,9 +214,9 @@ cython==3.2.4 \ # via # pyarrow # uvloop -dunamai==1.26.0 \ - --hash=sha256:5396ac43aa20ed059040034e9f9798c7464cf4334c6fc3da3732e29273a2f97d \ - --hash=sha256:f584edf0fda0d308cce0961f807bc90a8fe3d9ff4d62f94e72eca7b43f0ed5f6 +dunamai==1.26.1 \ + --hash=sha256:2727d939c5b4257cb01ea404372803b477f5176e5a347c43beaf89cd5072e853 \ + --hash=sha256:3b46007bd65b00b4824ead0a1aee365fd22d0ec2b9c219497d4fd48f52860c8b # via uv-dynamic-versioning expandvars==1.1.2 \ --hash=sha256:6c5822b7b756a99a356b915dd1267f52ab8a4efaa135963bd7f4bd5d368f71d7 \ @@ -257,9 +257,9 @@ gitdb==4.0.12 \ --hash=sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571 \ --hash=sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf # via gitpython -gitpython==3.1.46 \ - --hash=sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f \ - --hash=sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058 +gitpython==3.1.47 \ + --hash=sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905 \ + --hash=sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd # via pymilvus hatch-fancy-pypi-readme==25.1.0 \ --hash=sha256:9c58ed3dff90d51f43414ce37009ad1d5b0f08ffc9fc216998a06380f01c0045 \ @@ -326,97 +326,164 @@ jinja2==3.1.6 \ --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 # via uv-dynamic-versioning -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +libcst==1.8.6 \ + --hash=sha256:04030ea4d39d69a65873b1d4d877def1c3951a7ada1824242539e399b8763d30 \ + --hash=sha256:06fc56335a45d61b7c1b856bfab4587b84cfe31e9d6368f60bb3c9129d900f58 \ + --hash=sha256:089c58e75cb142ec33738a1a4ea7760a28b40c078ab2fd26b270dac7d2633a4d \ + --hash=sha256:08bd63a8ce674be431260649e70fca1d43f1554f1591eac657f403ff8ef82c7a \ + --hash=sha256:0c13d5bd3d8414a129e9dccaf0e5785108a4441e9b266e1e5e9d1f82d1b943c9 \ + --hash=sha256:0cbe17067055829607c5ba4afa46bfa4d0dd554c0b5a583546e690b7367a29b6 \ + --hash=sha256:16cfe0cfca5fd840e1fb2c30afb628b023d3085b30c3484a79b61eae9d6fe7ba \ + --hash=sha256:1a3a5e4ee870907aa85a4076c914ae69066715a2741b821d9bf16f9579de1105 \ + --hash=sha256:1dc3b897c8b0f7323412da3f4ad12b16b909150efc42238e19cbf19b561cc330 \ + --hash=sha256:203ec2a83f259baf686b9526268cd23d048d38be5589594ef143aee50a4faf7e \ + --hash=sha256:207481197afd328aa91d02670c15b48d0256e676ce1ad4bafb6dc2b593cc58f1 \ + --hash=sha256:25eaeae6567091443b5374b4c7d33a33636a2d58f5eda02135e96fc6c8807786 \ + --hash=sha256:25fc7a1303cad7639ad45ec38c06789b4540b7258e9a108924aaa2c132af4aca \ + --hash=sha256:2f04d3672bde1704f383a19e8f8331521abdbc1ed13abb349325a02ac56e5012 \ + --hash=sha256:351ab879c2fd20d9cb2844ed1ea3e617ed72854d3d1e2b0880ede9c3eea43ba8 \ + --hash=sha256:36473e47cb199b7e6531d653ee6ffed057de1d179301e6c67f651f3af0b499d6 \ + --hash=sha256:3649a813660fbffd7bc24d3f810b1f75ac98bd40d9d6f56d1f0ee38579021073 \ + --hash=sha256:375965f34cc6f09f5f809244d3ff9bd4f6cb6699f571121cebce53622e7e0b86 \ + --hash=sha256:3a926a4b42015ee24ddfc8ae940c97bd99483d286b315b3ce82f3bafd9f53474 \ + --hash=sha256:3f4fbb7f569e69fd9e89d9d9caa57ca42c577c28ed05062f96a8c207594e75b8 \ + --hash=sha256:42a4f68121e2e9c29f49c97f6154e8527cd31021809cc4a941c7270aa64f41aa \ + --hash=sha256:44f38139fa95e488db0f8976f9c7ca39a64d6bc09f2eceef260aa1f6da6a2e42 \ + --hash=sha256:455f49a93aea4070132c30ebb6c07c2dea0ba6c1fde5ffde59fc45dbb9cfbe4b \ + --hash=sha256:4d7bbdd35f3abdfb5ac5d1a674923572dab892b126a58da81ff2726102d6ec2e \ + --hash=sha256:4fc3fef8a2c983e7abf5d633e1884c5dd6fa0dcb8f6e32035abd3d3803a3a196 \ + --hash=sha256:536567441182a62fb706e7aa954aca034827b19746832205953b2c725d254a93 \ + --hash=sha256:5432e785322aba3170352f6e72b32bea58d28abd141ac37cc9b0bf6b7c778f58 \ + --hash=sha256:55ec021a296960c92e5a33b8d93e8ad4182b0eab657021f45262510a58223de1 \ + --hash=sha256:59a7e388c57d21d63722018978a8ddba7b176e3a99bd34b9b84a576ed53f2978 \ + --hash=sha256:5dcaaebc835dfe5755bc85f9b186fb7e2895dda78e805e577fef1011d51d5a5c \ + --hash=sha256:6366ab2107425bf934b0c83311177f2a371bfc757ee8c6ad4a602d7cbcc2f363 \ + --hash=sha256:6421a930b028c5ef4a943b32a5a78b7f1bf15138214525a2088f11acbb7d3d64 \ + --hash=sha256:6609291c41f7ad0bac570bfca5af8fea1f4a27987d30a1fa8b67fe5e67e6c78d \ + --hash=sha256:6a65f844d813ab4ef351443badffa0ae358f98821561d19e18b3190f59e71996 \ + --hash=sha256:6aa11df6c58812f731172b593fcb485d7ba09ccc3b52fea6c7f26a43377dc748 \ + --hash=sha256:6b23d14a7fc0addd9795795763af26b185deb7c456b1e7cc4d5228e69dab5ce8 \ + --hash=sha256:6cad63e3a26556b020b634d25a8703b605c0e0b491426b3e6b9e12ed20f09100 \ + --hash=sha256:6d8b67874f2188399a71a71731e1ba2d1a2c3173b7565d1cc7ffb32e8fbaba5b \ + --hash=sha256:72cca15800ffc00ba25788e4626189fe0bc5fe2a0c1cb4294bce2e4df21cc073 \ + --hash=sha256:7445479ebe7d1aff0ee094ab5a1c7718e1ad78d33e3241e1a1ec65dcdbc22ffb \ + --hash=sha256:7f04febcd70e1e67917be7de513c8d4749d2e09206798558d7fe632134426ea4 \ + --hash=sha256:8066f1b70f21a2961e96bedf48649f27dfd5ea68be5cd1bed3742b047f14acde \ + --hash=sha256:819c8081e2948635cab60c603e1bbdceccdfe19104a242530ad38a36222cb88f \ + --hash=sha256:85b7025795b796dea5284d290ff69de5089fc8e989b25d6f6f15b6800be7167f \ + --hash=sha256:87e74f7d7dfcba9efa91127081e22331d7c42515f0a0ac6e81d4cf2c3ed14661 \ + --hash=sha256:8a434c521fadaf9680788b50d5c21f4048fa85ed19d7d70bd40549fbaeeecab1 \ + --hash=sha256:98fa1ca321c81fb1f02e5c43f956ca543968cc1a30b264fd8e0a2e1b0b0bf106 \ + --hash=sha256:a20c5182af04332cc94d8520792befda06d73daf2865e6dddc5161c72ea92cb9 \ + --hash=sha256:b0d8c364c44ae343937f474b2e492c1040df96d94530377c2f9263fb77096e4f \ + --hash=sha256:b188e626ce61de5ad1f95161b8557beb39253de4ec74fc9b1f25593324a0279c \ + --hash=sha256:b6c1248cc62952a3a005792b10cdef2a4e130847be9c74f33a7d617486f7e532 \ + --hash=sha256:ba9ab2b012fbd53b36cafd8f4440a6b60e7e487cd8b87428e57336b7f38409a4 \ + --hash=sha256:bb9b4077bdf8857b2483879cbbf70f1073bc255b057ec5aac8a70d901bb838e9 \ + --hash=sha256:bdb14bc4d4d83a57062fed2c5da93ecb426ff65b0dc02ddf3481040f5f074a82 \ + --hash=sha256:bff00e1c766658adbd09a175267f8b2f7616e5ee70ce45db3d7c4ce6d9f6bec7 \ + --hash=sha256:c0a0cc80aebd8aa15609dd4d330611cbc05e9b4216bcaeabba7189f99ef07c28 \ + --hash=sha256:c188d06b583900e662cd791a3f962a8c96d3dfc9b36ea315be39e0a4c4792ebf \ + --hash=sha256:c41c76e034a1094afed7057023b1d8967f968782433f7299cd170eaa01ec033e \ + --hash=sha256:c9d7aeafb1b07d25a964b148c0dda9451efb47bbbf67756e16eeae65004b0eb5 \ + --hash=sha256:cb2679ef532f9fa5be5c5a283b6357cb6e9888a8dd889c4bb2b01845a29d8c0b \ + --hash=sha256:da95b38693b989eaa8d32e452e8261cfa77fe5babfef1d8d2ac25af8c4aa7e6d \ + --hash=sha256:e00e275d4ba95d4963431ea3e409aa407566a74ee2bf309a402f84fc744abe47 \ + --hash=sha256:f1472eeafd67cdb22544e59cf3bfc25d23dc94058a68cf41f6654ff4fcb92e09 \ + --hash=sha256:f729c37c9317126da9475bdd06a7208eb52fcbd180a6341648b45a56b4ba708b \ + --hash=sha256:fea5c7fa26556eedf277d4f72779c5ede45ac3018650721edd77fd37ccd4a2d4 + # via pyarrow +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy markupsafe==3.0.3 \ --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ @@ -509,21 +576,21 @@ markupsafe==3.0.3 \ --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 # via jinja2 -maturin==1.12.6 \ - --hash=sha256:06fc8d089f98623ce924c669b70911dfed30f9a29956c362945f727f9abc546b \ - --hash=sha256:2cb41139295eed6411d3cdafc7430738094c2721f34b7eeb44f33cac516115dc \ - --hash=sha256:351f3af1488a7cbdcff3b6d8482c17164273ac981378a13a4a9937a49aec7d71 \ - --hash=sha256:3f32e0a3720b81423c9d35c14e728cb1f954678124749776dc72d533ea1115e8 \ - --hash=sha256:6892b4176992fcc143f9d1c1c874a816e9a041248eef46433db87b0f0aff4278 \ - --hash=sha256:6dbddfe4dc7ddee60bbac854870bd7cfec660acb54d015d24597d59a1c828f61 \ - --hash=sha256:75133e56274d43b9227fd49dca9a86e32f1fd56a7b55544910c4ce978c2bb5aa \ - --hash=sha256:8fdb0f63e77ee3df0f027a120e9af78dbc31edf0eb0f263d55783c250c33b728 \ - --hash=sha256:977290159d252db946054a0555263c59b3d0c7957135c69e690f4b1558ee9983 \ - --hash=sha256:bae91976cdc8148038e13c881e1e844e5c63e58e026e8b9945aa2d19b3b4ae89 \ - --hash=sha256:c0c742beeeef7fb93b6a81bd53e75507887e396fd1003c45117658d063812dad \ - --hash=sha256:d37be3a811a7f2ee28a0fa0964187efa50e90f21da0c6135c27787fa0b6a89db \ - --hash=sha256:e90dc12bc6a38e9495692a36c9e231c4d7e0c9bfde60719468ab7d8673db3c45 \ - --hash=sha256:fa84b7493a2e80759cacc2e668fa5b444d55b9994e90707c42904f55d6322c1e +maturin==1.13.1 \ + --hash=sha256:001741c6cff56aa8ea59a0d78ae990c0550d0e3e82b00b683eedb4158a8ef7e6 \ + --hash=sha256:01c845825c917c07c1d0b2c9032c59c16a7d383d1e649a46481d3e5693c2750f \ + --hash=sha256:2839024dcd65776abb4759e5bca29941971e095574162a4d335191da4be9ff24 \ + --hash=sha256:3da18cccf2f683c0977bff9146a0908d6ffce836d600665736ac01679f588cb9 \ + --hash=sha256:416e4e01cb88b798e606ee43929df897e42c1647b722ef68283816cca99a8742 \ + --hash=sha256:6b1e5916a253243e8f5f9e847b62bbc98420eec48c9ce2e2e8724c6da89d359b \ + --hash=sha256:72888e87819ce546d0d2df900e4b385e4ef299077d92ee37b48923a5602dae94 \ + --hash=sha256:98b5fcf1a186c217830a8295ecc2989c6b1cf50945417adfc15252107b9475b7 \ + --hash=sha256:9a87ff3b8e4d1c6eac33ebfe8e261e8236516d98d45c0323550621819b5a1a2f \ + --hash=sha256:a2017d2281203d0c6570240e7d746564d766d756105823b7de68bda6ae722711 \ + --hash=sha256:c1490584f3c70af45466ee99065b49e6657ebdccac6b10571bb44681309c9396 \ + --hash=sha256:c6a720b252c99de072922dbe4432ab19662b6f80045b0355fec23bdfccb450da \ + --hash=sha256:dc91031e0619c1e28730279ef9ee5f106c9b9ec806b013f888676b242f892eb7 \ + --hash=sha256:f69093ed4a0e6464e52a7fc26d714f859ce15630ec8070743398c6bf41f38a9e # via # cryptography # pydantic-core @@ -623,9 +690,9 @@ numpy==2.4.4 \ # via # pandas # pyarrow -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # hatchling # meson-python @@ -635,9 +702,9 @@ packaging==26.0 \ # setuptools-scm # vcs-versioning # wheel -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via # hatchling # mypy @@ -666,13 +733,13 @@ poetry-core==2.3.2 \ # pkgconfig # rich # tomlkit -pybind11-global==3.0.3 \ - --hash=sha256:141adb150fdb84f6eba3e27241da886f4582574a3d1c30568bf33c1ed3ec8b82 \ - --hash=sha256:7a75ee81e903ea15bdf05db1342c37400751a72316b6620c800b66d70be45632 +pybind11-global==3.0.4 \ + --hash=sha256:95b693c3d646c6b7217a97156a36b6d40305505d4a5a728082da6fe75ada29f5 \ + --hash=sha256:a73e2ebd29f4ee5d7c754b495d555cd703f2cd26c84abe360ee56411dcd7834d # via pybind11 -pybind11==3.0.3 \ - --hash=sha256:00471cdb816882c484708bc5dde80815c8c11cea540ab2cc6410f5ddea434755 \ - --hash=sha256:fb5f8e4a64946b4dcc0451c83a8c384f803bc0a62dd1ba02f199e97dbc9aad4c +pybind11==3.0.4 \ + --hash=sha256:3286b59c8a774b9ee650169302dd5a4eedc30a8617905a0560dd8ee44775130c \ + --hash=sha256:961720ee652da51d531b7b2451a6bd2bc042b0106e6d9baa48ecb7d58034ce63 # via duckdb pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ @@ -682,12 +749,88 @@ pyproject-metadata==0.11.0 \ --hash=sha256:85bbecca8694e2c00f63b492c96921d6c228454057c88e7c352b2077fcaa4096 \ --hash=sha256:c72fa49418bb7c5a10f25e050c418009898d1c051721d19f98a6fb6da59a66cf # via meson-python +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via libcst scikit-build-core==0.12.2 \ --hash=sha256:562e0bbc9de1a354c87825ccf732080268d6582a0200f648e8c4a2dcb1e3736d \ --hash=sha256:6ea4730da400f9a998ec3287bd3ebc1d751fe45ad0a93451bead8618adbc02b1 # via # duckdb # patchelf + # pyarrow # pybind11 # pybind11-global semantic-version==2.10.0 \ @@ -701,7 +844,9 @@ setuptools-git-versioning==3.0.1 \ setuptools-rust==1.12.1 \ --hash=sha256:85ae70989d96c9cfeb5ef79cf3bac2d5200bc5564f720a06edceedbdf6664640 \ --hash=sha256:b7ebd6a182e7aefa97a072e880530c9b0ec8fcca8617e0bb8ff299c1a064f693 - # via maturin + # via + # libcst + # maturin setuptools-scm==10.0.5 \ --hash=sha256:bbba8fe754516cdefd017f4456721775e6ef9662bd7887fb52ae26813d4838c3 \ --hash=sha256:f611037d8aae618221503b8fa89319f073438252ae3420e01c9ceec249131a0a @@ -712,6 +857,7 @@ setuptools-scm==10.0.5 \ # hatch-vcs # httpx-sse # importlib-metadata + # libcst # pluggy # pyarrow # pybindgen @@ -799,9 +945,9 @@ types-psutil==7.0.0.20250218 \ --hash=sha256:1447a30c282aafefcf8941ece854e1100eee7b0296a9d9be9977292f0269b121 \ --hash=sha256:1e642cdafe837b240295b23b1cbd4691d80b08a07d29932143cbbae30eb0db9c # via mypy -types-setuptools==82.0.0.20260402 \ - --hash=sha256:4b9a9f6c3c4c65107a3956ad6a6acbccec38e398ff6d5f78d5df7f103dadb8d6 \ - --hash=sha256:63d2b10ba7958396ad79bbc24d2f6311484e452daad4637ffd40407983a27069 +types-setuptools==82.0.0.20260408 \ + --hash=sha256:036c68caf7e672a699f5ebbf914708d40644c14e05298bc49f7272be91cf43d3 \ + --hash=sha256:ece0a215cdfa6463a65fd6f68bd940f39e455729300ddfe61cab1147ed1d2462 # via mypy typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -823,13 +969,14 @@ versioneer==0.29 \ # via # pandas # partd -wheel==0.46.3 \ - --hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d \ - --hash=sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803 +wheel==0.47.0 \ + --hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \ + --hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3 # via # async-timeout # google-crc32c # httpx-sse + # libcst # meson # mmh3 # pandas @@ -873,6 +1020,7 @@ setuptools==80.10.2 \ # gunicorn # httpx-sse # importlib-metadata + # libcst # librt # markupsafe # maturin @@ -891,7 +1039,6 @@ setuptools==80.10.2 \ # psycopg # psycopg-c # psycopg-pool - # pyarrow # pyasn1 # pyasn1-modules # pycparser @@ -915,7 +1062,6 @@ setuptools==80.10.2 \ # tqdm # trove-classifiers # typeguard - # types-pymysql # tzdata # ujson # uvloop @@ -934,4 +1080,5 @@ setuptools==82.0.1 \ --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb # via # python-dateutil + # types-pymysql # types-setuptools diff --git a/sdk/python/requirements/py3.11-minimal-sdist-requirements.txt b/sdk/python/requirements/py3.11-minimal-sdist-requirements.txt index 34cda10c5fb..0d51f8693d4 100644 --- a/sdk/python/requirements/py3.11-minimal-sdist-requirements.txt +++ b/sdk/python/requirements/py3.11-minimal-sdist-requirements.txt @@ -206,9 +206,9 @@ calver==2025.3.31 \ --hash=sha256:07511edf5e7fa75ae97445c8c5921240e0fe62937289a3ebe6963eddd3c691b6 \ --hash=sha256:255d1a70bba8f97dc1eee3af4240ed35980508da69257feef94c79e5c6545fc7 # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # httpcore # httpx @@ -436,9 +436,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -452,56 +452,56 @@ colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via feast (pyproject.toml) -cryptography==46.0.6 \ - --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ - --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ - --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ - --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ - --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ - --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ - --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ - --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ - --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ - --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ - --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ - --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ - --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ - --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ - --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ - --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ - --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ - --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ - --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ - --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ - --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ - --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ - --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ - --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ - --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ - --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ - --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ - --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ - --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ - --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ - --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ - --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ - --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ - --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ - --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ - --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ - --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ - --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ - --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ - --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 +cryptography==47.0.0 \ + --hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \ + --hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \ + --hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \ + --hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \ + --hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \ + --hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \ + --hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \ + --hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \ + --hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \ + --hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \ + --hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \ + --hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \ + --hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \ + --hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \ + --hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \ + --hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \ + --hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \ + --hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \ + --hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \ + --hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \ + --hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \ + --hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \ + --hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \ + --hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \ + --hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \ + --hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \ + --hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \ + --hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \ + --hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \ + --hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \ + --hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \ + --hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \ + --hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \ + --hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \ + --hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \ + --hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \ + --hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \ + --hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \ + --hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \ + --hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \ + --hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \ + --hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \ + --hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \ + --hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \ + --hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \ + --hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \ + --hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \ + --hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \ + --hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736 # via # google-auth # pyjwt @@ -591,50 +591,50 @@ docutils==0.22.4 \ --hash=sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968 \ --hash=sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de # via sphinx -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ --hash=sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286 # via kubernetes -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -642,9 +642,9 @@ fastapi-mcp==0.4.0 \ --hash=sha256:d4a3fe7966af24d44e4b412720561c95eb12bed999a4443a88221834b3b15aec \ --hash=sha256:d4ca9410996f4c7b8ea0d7b20fdf79878dc359ebf89cbf3b222e0b675a55097d # via feast (pyproject.toml) -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via snowflake-connector-python flit-core==3.12.0 \ --hash=sha256:18f63100d6f94385c6ed57a72073443e1a71a4acb4339491615d0f16d6ff01b2 \ @@ -790,9 +790,9 @@ fsspec==2024.9.0 \ # via # feast (pyproject.toml) # dask -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -802,9 +802,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-datastore # google-cloud-storage # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -904,60 +904,66 @@ googleapis-common-protos[grpc]==1.74.0 \ # google-api-core # grpc-google-iam-v1 # grpcio-status -greenlet==3.3.2 \ - --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ - --hash=sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082 \ - --hash=sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b \ - --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ - --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ - --hash=sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727 \ - --hash=sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e \ - --hash=sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2 \ - --hash=sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f \ - --hash=sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327 \ - --hash=sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd \ - --hash=sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2 \ - --hash=sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070 \ - --hash=sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99 \ - --hash=sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be \ - --hash=sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79 \ - --hash=sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7 \ - --hash=sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e \ - --hash=sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf \ - --hash=sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f \ - --hash=sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506 \ - --hash=sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a \ - --hash=sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395 \ - --hash=sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4 \ - --hash=sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca \ - --hash=sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492 \ - --hash=sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab \ - --hash=sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358 \ - --hash=sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce \ - --hash=sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5 \ - --hash=sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef \ - --hash=sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d \ - --hash=sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac \ - --hash=sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55 \ - --hash=sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124 \ - --hash=sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4 \ - --hash=sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986 \ - --hash=sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd \ - --hash=sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f \ - --hash=sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb \ - --hash=sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4 \ - --hash=sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13 \ - --hash=sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab \ - --hash=sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff \ - --hash=sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a \ - --hash=sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9 \ - --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 \ - --hash=sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd \ - --hash=sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71 \ - --hash=sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92 \ - --hash=sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643 \ - --hash=sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54 \ - --hash=sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9 +greenlet==3.4.0 \ + --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \ + --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \ + --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \ + --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \ + --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \ + --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \ + --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \ + --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \ + --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \ + --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \ + --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \ + --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \ + --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \ + --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \ + --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \ + --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \ + --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \ + --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \ + --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \ + --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \ + --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \ + --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \ + --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \ + --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \ + --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \ + --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \ + --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \ + --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \ + --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \ + --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \ + --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \ + --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \ + --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \ + --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \ + --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \ + --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \ + --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \ + --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \ + --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \ + --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \ + --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \ + --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \ + --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \ + --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \ + --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \ + --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \ + --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \ + --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \ + --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \ + --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \ + --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \ + --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \ + --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \ + --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \ + --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \ + --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \ + --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \ + --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \ + --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf # via feast (pyproject.toml) grpc-google-iam-v1==0.14.4 \ --hash=sha256:392b3796947ed6334e61171d9ab06bf7eb357f554e5fc7556ad7aab6d0e17038 \ @@ -1228,9 +1234,9 @@ ibis-framework[duckdb]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1272,97 +1278,97 @@ kubernetes==35.0.0 \ --hash=sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d \ --hash=sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee # via feast (pyproject.toml) -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -1741,51 +1747,51 @@ multidict==6.7.1 \ # aiobotocore # aiohttp # yarl -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -1875,9 +1881,9 @@ oauthlib==3.3.1 \ --hash=sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9 \ --hash=sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1 # via requests-oauthlib -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # db-dtypes @@ -1957,9 +1963,9 @@ pandas==2.3.3 \ # pandas-gbq # pymilvus # snowflake-connector-python -pandas-gbq==0.34.1 \ - --hash=sha256:6bea5b85937251b976cf9db38151ea59abbff98771179183488d4614694bff67 \ - --hash=sha256:b74932c6ee35dfc81582f39c792e3a68c9ef9bee8c85f25667d9d05dfadd0daf +pandas-gbq==0.35.0 \ + --hash=sha256:258de481019566611031919997bf9c1ece4ca30a4dd02d3fc3664b251d446182 \ + --hash=sha256:596c908487ef0649a161e86ef272c00c267e581b95dc5ee0dc3518545b33bcfc # via google-cloud-bigquery parsy==2.2 \ --hash=sha256:5e981613d9d2d8b68012d1dd0afe928967bea2e4eefdb76c2f545af0dd02a9e7 \ @@ -1980,24 +1986,24 @@ patchelf==0.17.2.4 \ --hash=sha256:d842b51f0401460f3b1f3a3a67d2c266a8f515a5adfbfa6e7b656cb3ac2ed8bc \ --hash=sha256:d9b35ebfada70c02679ad036407d9724ffe1255122ba4ac5e4be5868618a5689 # via feast (pyproject.toml) -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via # hatchling # mypy # scikit-build-core -platformdirs==4.9.4 \ - --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ - --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 +platformdirs==4.9.6 \ + --hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \ + --hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917 # via snowflake-connector-python pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 # via hatchling -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) propcache==0.4.1 \ --hash=sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e \ @@ -2193,57 +2199,57 @@ psycopg-pool==3.3.0 \ --hash=sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 \ --hash=sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5 # via psycopg -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -2272,141 +2278,140 @@ pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi # fastapi-mcp # mcp # pydantic-settings -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # fastapi-mcp # mcp @@ -2436,9 +2441,9 @@ pymysql==1.1.2 \ --hash=sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03 \ --hash=sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9 # via feast (pyproject.toml) -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python pyproject-metadata==0.11.0 \ --hash=sha256:85bbecca8694e2c00f63b492c96921d6c228454057c88e7c352b2077fcaa4096 \ @@ -2461,9 +2466,9 @@ python-dotenv==1.2.2 \ # pydantic-settings # pymilvus # uvicorn -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp pytz==2026.1.post1 \ --hash=sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1 \ @@ -2579,9 +2584,9 @@ requests-oauthlib==2.0.0 \ # via # google-auth-oauthlib # kubernetes -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 # via # fastapi-mcp # ibis-framework @@ -2803,74 +2808,74 @@ sphinxcontrib-serializinghtml==2.0.0 \ --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \ --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d # via sphinx -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via ibis-framework sse-starlette==3.3.4 \ --hash=sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1 \ @@ -2969,17 +2974,17 @@ typeguard==4.5.1 \ --hash=sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40 \ --hash=sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274 # via feast (pyproject.toml) -typer==0.24.1 \ - --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e \ - --hash=sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45 +typer==0.24.2 \ + --hash=sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8 \ + --hash=sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480 # via fastapi-mcp types-psutil==7.0.0.20250218 \ --hash=sha256:1447a30c282aafefcf8941ece854e1100eee7b0296a9d9be9977292f0269b121 \ --hash=sha256:1e642cdafe837b240295b23b1cbd4691d80b08a07d29932143cbbae30eb0db9c # via feast (pyproject.toml) -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -3010,9 +3015,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # ibis-framework # pandas @@ -3561,9 +3566,9 @@ yarl==1.23.0 \ --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \ --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d # via aiohttp -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata # The following packages were excluded from the output: diff --git a/sdk/python/requirements/py3.11-requirements.txt b/sdk/python/requirements/py3.11-requirements.txt index ec293cec030..8e30a7275c4 100644 --- a/sdk/python/requirements/py3.11-requirements.txt +++ b/sdk/python/requirements/py3.11-requirements.txt @@ -24,9 +24,9 @@ bigtree==1.4.0 \ --hash=sha256:d0d99550ae64ce4529f132602ab875c2ab472c96c942f5704f8c72a17450d3ea \ --hash=sha256:e5ae2e948168da671d99601c9ed87ab3b48d9d4ea8a98f111e5748e98064c31c # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via requests charset-normalizer==3.4.7 \ --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \ @@ -159,9 +159,9 @@ charset-normalizer==3.4.7 \ --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \ --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464 # via requests -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -182,9 +182,9 @@ dill==0.3.9 \ --hash=sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a \ --hash=sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c # via feast (pyproject.toml) -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via feast (pyproject.toml) fsspec==2026.3.0 \ --hash=sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41 \ @@ -245,9 +245,9 @@ httptools==0.7.1 \ --hash=sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec \ --hash=sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362 # via uvicorn -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # requests @@ -267,97 +267,97 @@ jsonschema-specifications==2025.9.1 \ --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d # via jsonschema -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -563,51 +563,51 @@ mmh3==5.2.1 \ --hash=sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a \ --hash=sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b # via feast (pyproject.toml) -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -690,9 +690,9 @@ numpy==2.4.4 \ # feast (pyproject.toml) # dask # pandas -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # gunicorn @@ -759,13 +759,13 @@ partd==1.4.2 \ --hash=sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f \ --hash=sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c # via dask -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via mypy -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) protobuf==7.34.1 \ --hash=sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a \ @@ -800,188 +800,187 @@ psutil==7.2.2 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 # via feast (pyproject.toml) -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic pygments==2.20.0 \ --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ @@ -1214,70 +1213,70 @@ six==1.17.0 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 # via python-dateutil -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) starlette==1.0.0 \ --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \ @@ -1329,9 +1328,9 @@ typing-inspection==0.4.2 \ # via # fastapi # pydantic -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via pandas urllib3==2.6.3 \ --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ @@ -1572,7 +1571,7 @@ websockets==16.0 \ --hash=sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da \ --hash=sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4 # via uvicorn -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata diff --git a/sdk/python/requirements/py3.12-ci-requirements.txt b/sdk/python/requirements/py3.12-ci-requirements.txt index 8fe44b610b6..dac1e275d5e 100644 --- a/sdk/python/requirements/py3.12-ci-requirements.txt +++ b/sdk/python/requirements/py3.12-ci-requirements.txt @@ -366,49 +366,44 @@ botocore==1.38.46 \ # moto # s3transfer # snowflake-connector-python -build==1.4.2 \ - --hash=sha256:35b14e1ee329c186d3f08466003521ed7685ec15ecffc07e68d706090bf161d1 \ - --hash=sha256:7a4d8651ea877cb2a89458b1b198f2e69f536c95e89129dbf5d448045d60db88 +build==1.4.4 \ + --hash=sha256:8c3f48a6090b39edec1a273d2d57949aaf13723b01e02f9d518396887519f64d \ + --hash=sha256:f832ae053061f3fb524af812dc94b8b84bac6880cd587630e3b5d91a6a9c1703 # via # feast (pyproject.toml) # pip-tools # singlestoredb -cassandra-driver==3.29.3 \ - --hash=sha256:064bf45d3ca87239e11168c0110676fc64f7fdbddb4bcba9be787b8ad5f6d734 \ - --hash=sha256:0785f6e0986089e922378ae3b64b5f696440aeb595fb84c2cf3ccef220c6ae91 \ - --hash=sha256:158f7e5cb894a76a592aa0ca659a8e7c2a57ef603e04c07bbbc289a70e9ac893 \ - --hash=sha256:1c241ba08473baf31a333feb59793190d01625541c2368d3bbb0f43a586f1d6a \ - --hash=sha256:26013d768b2ea4728c09144b08c0eb86ad692e85cb15f4e52e3107abca83683c \ - --hash=sha256:27adf8869937461ad08c5fefb47857532e467b408db496db4dbf8b132a4bd623 \ - --hash=sha256:281f67af1b8df88741eef551afbb49f78e4f366a7ab23e7060a1f0d6ba655752 \ - --hash=sha256:29fc241475801872dc27c3dd1a3976373536223dd4fd1c01868ff86bdbbfd48b \ - --hash=sha256:2b72312a8b62a905da6133effbba9b0731c8e30af96e10ca77fc5c34532c6827 \ - --hash=sha256:2cb72808dfc46c40a6ee352ace181ce3170adde1cfd1447da91709a8cf482e20 \ - --hash=sha256:38216e13d6f2e0d4513a5b8806e70ce4a8f28a82962793a67371582fc2c7141b \ - --hash=sha256:3f654b01d8d49f68deedfaff1edcff314e3103d29130b2a034df6c490c522351 \ - --hash=sha256:51d6a5390e2454b599500049f0a5c72aa701db155c1e542f9a1157c1c45814b1 \ - --hash=sha256:54afde4aaa5b55fbc2c075e1c55fb14a5739459428f3bb81f849ad020f7d5bcf \ - --hash=sha256:572bd5a01089ab92da12f4f52b32b878547bbc544a798d8cfd042e7fc2601c75 \ - --hash=sha256:5a0113020d86e8f61c7a2ae3d508720cd036df7462a55926b85dd97ada27e143 \ - --hash=sha256:5f9858b5ccdf75dd89c20d74474b59dd3a2e2f86c7251b310011c46acdef3874 \ - --hash=sha256:638047c1f70fb14c9d8f743931d4f4f42aff6793b47afded3097c002ef8c1165 \ - --hash=sha256:63adca0f9219be3fe8789f4aa7b77c5f6a7bf65d6442959db52c653140ca4185 \ - --hash=sha256:7552fb7189acd06161f8feac7045a387dc9e03b3b9f7dcb5675178906cee792e \ - --hash=sha256:7a2f371af54cd1d153ef373a733889ebfbcc9c30e00429fc12a2569bad9239e1 \ - --hash=sha256:84b24f69a7bbe76302330d47422a7fcc1998a6a96ffd414a795d7d95992b49cb \ - --hash=sha256:891a1b6a111a591ad9f1c9e088846848dc9e6be030a6086c8c3aa5d2d837f266 \ - --hash=sha256:96ad742f5cbfb771df512959ab5de36e248ce9aa2c487fd81c37d5c0a627c094 \ - --hash=sha256:9abedc832e9a6636741299aae46c032d8c1248b507d8cebbaa2f48ec202904bc \ - --hash=sha256:9b7032b44769c454e96aa11483bfd167a87ea341268f1075b0ff84f780c910a9 \ - --hash=sha256:c935431682557ffcd3efc1c7bcb01b0f6769a1c90751a7154d5e3c905a6a2042 \ - --hash=sha256:e1d09691d757f5b1900a98cc3b6cc7d8506683a2188c01eca86545f91edbbaf5 \ - --hash=sha256:facd488c2b9be8bffcad5903566581e96d2863d2ec4bcad7f114d1b2b2f39ad0 \ - --hash=sha256:fcf45725ae1751cb934b9b827a7d9cd899bbd09eb1ad28e2160b4584de35ba77 \ - --hash=sha256:ff6b82ee4533f6fd4474d833e693b44b984f58337173ee98ed76bce08721a636 +cassandra-driver==3.30.0 \ + --hash=sha256:0c28a8e84917acebecbaed39844047c2f135739c3627dd7b9f8541af33e11df3 \ + --hash=sha256:0f4225082a11d9529416c223553ab38a29c4e65da6646b40159c554480dc002c \ + --hash=sha256:136b46437b9902673264e101cdaab309d3e40607bff34430bda86b785badc6e4 \ + --hash=sha256:137498e2a9b6f578d1902e1af8a988e50b8fe134c76a176f1b8a774e906bc66c \ + --hash=sha256:17fb53587c9fc6a27b5c4a89b4f3d9169be43fc572d6f3f67494aa74708be936 \ + --hash=sha256:1d64cbdce764c33e284d339b9a749736d68971edf8b537888f2d13c4b0d1313f \ + --hash=sha256:212af4d8ff934c30538f4bdf7da61f14dc9a30349f6cac2161c8125e56fad928 \ + --hash=sha256:2637644eac9274e46b0c2a7f729158bdf8582b6842dc48e18297211dd3ee1fec \ + --hash=sha256:289e86c81be2543cb9055600c0819850db921e6e138a84e5c88ec160662c7207 \ + --hash=sha256:2a0679ebcfdcecb3763c690b5bc6a517e0c0803f7bc88e0a6c793e5e421b558a \ + --hash=sha256:385134eba72f048707cd800de0a61cf3c23246113edffe9bc6bc2eb86282d26b \ + --hash=sha256:5c6cbb396ad6fe456efc799d3b8b6bda360ffc06552c5be2ce1a88ac381a305c \ + --hash=sha256:61d7eeb17d8f76d5b4a9b1239145250f2a9f7bf949c30e2cc36196b5a0523ce1 \ + --hash=sha256:6a5c8982f2b9eb4e789fc12cdd930b1e1511b6d046dde31d0703f855745556a3 \ + --hash=sha256:6d449f49ce866ac20a1c3d80b1f9245ecdfd1e67b843dccd3d6eccdfe519c02e \ + --hash=sha256:7e4cfd6ec3023576ed0ffa34882d9778e4bacfd918048ae9139ccdd00628ed85 \ + --hash=sha256:83a9148d408a3dbb48ea1802d643d60fa53cd69dc7b9a244511ecf5b917e4f53 \ + --hash=sha256:8c4acd28791854c23ca68be50a7a750c9413ba80fec0ca5c27c2be05f6f3fe0a \ + --hash=sha256:8d5e3575ec01d8c043b56ff25de6f61ff4c9ed5cb3ea4c3d9df98def71ba710c \ + --hash=sha256:923a6e1c3fa5f98f846a028b1a7207ec9e7d8cfa54ea47a507d41122efa2f54f \ + --hash=sha256:c1b4aa6c7706dec839134adb6a2094d90c5f6f35efa08028ed6aae6e67c8643e \ + --hash=sha256:c64e20bf46b49f8ef64569208d4a395b0928c27d5960559922a2d13471924d0d \ + --hash=sha256:d2f9e00127f70dff42d4ef932df8a6b81170c2861d4e75c8b13f4b4816b4450c \ + --hash=sha256:d73c0429813045ba86b92fc033fbcfd495aa10e9d4a40fe30b6e9dfe8b5d3ab4 \ + --hash=sha256:e12dfcd3f0074c16f4bfe650242edb406b935864373ae86160e09e3f5e437e84 \ + --hash=sha256:ff2e9fbdc1be54c1d041ea3f7d09812442f334be14bb5ad7aede175544765d25 # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # clickhouse-connect # docling @@ -646,9 +641,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -1006,9 +1001,9 @@ db-dtypes==1.5.1 \ # via # google-cloud-bigquery # pandas-gbq -dbt-artifacts-parser==0.13.0 \ - --hash=sha256:304f2b857650566fed4ed8b976ed3582332eda3cedfe7167158dbbcfced3fe47 \ - --hash=sha256:55498e8bd0d9064d56617f9c714ced8607d94ccb61e70d4b49dcfd8a28a030d8 +dbt-artifacts-parser==0.13.1 \ + --hash=sha256:c341730fa34ebb38cc7d2de0282e8b713e2fc65fc6577f0d944f8abee8949dc4 \ + --hash=sha256:c7a3c4e309ae2d7d566a615e92043b0d346a77998203b0cc466234717b806e40 # via feast (pyproject.toml) debugpy==1.8.20 \ --hash=sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad \ @@ -1061,6 +1056,10 @@ deltalake==0.25.5 \ --hash=sha256:cb1c7e826fd7c3bdd3676c7471d3b551e1a3674e44cd8e3747a0017a2c0292b7 \ --hash=sha256:e8f0d24bf64455f702da8402307b22e01f91e0f76694f7c5e33c9513011e8d29 # via feast (pyproject.toml) +deprecated==1.3.1 \ + --hash=sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f \ + --hash=sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223 + # via cassandra-driver deprecation==2.1.0 \ --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a @@ -1076,6 +1075,12 @@ distlib==0.4.0 \ --hash=sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 \ --hash=sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d # via virtualenv +dnspython==2.8.0 \ + --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \ + --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f + # via + # feast (pyproject.toml) + # pymongo docker==7.1.0 \ --hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \ --hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0 @@ -1084,16 +1089,16 @@ docling==2.27.0 \ --hash=sha256:1288ed75b27e33bf94daff34faffc6d11b7d7ccc13e3df84fb24adad3991f72d \ --hash=sha256:faba35662612a2c687a3a463e501d95f645316436084af92a0442ce162429a3d # via feast (pyproject.toml) -docling-core[chunking]==2.71.0 \ - --hash=sha256:4761857816853b2b35263b5b4518e1ea6214e0565db0bbf1d929fb976665d1a0 \ - --hash=sha256:4caa9f50c68b9dd332584ae16170b36db05d773532b14d7078b580d89d8bd2a4 +docling-core[chunking]==2.74.1 \ + --hash=sha256:46bf298686f2c51ddd69b6935a27dff1cc80838f2f5f1a8823492d99cf1a357b \ + --hash=sha256:e6464078012b3d45f4e0accd101fcb277063903f355eabbb9aee8de00527a789 # via # docling # docling-ibm-models # docling-parse -docling-ibm-models==3.13.0 \ - --hash=sha256:a11acc6034b06e0bed8dc0ca1fa700615b8246eacce411619168e1f6562b0d0d \ - --hash=sha256:f402effae8a63b0e5c3b5ce13120601baa2cd8098beef1d53ab5a056443758d3 +docling-ibm-models==3.13.2 \ + --hash=sha256:195e02dd119df34d2ce5f76ac614da82825851013e4898db7b0468cdf8740a3d \ + --hash=sha256:5fa0838bf15a4e06d2fcb686d756a6f4c329ea0a8820d085f06d07abe96269ed # via docling docling-parse==4.7.3 \ --hash=sha256:1790e7e4ae202d67875c1c48fd6f8ef5c51d10b0c23157e4989b8673f2f31308 \ @@ -1128,42 +1133,42 @@ docutils==0.19 \ --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc # via sphinx -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ @@ -1225,9 +1230,9 @@ faiss-cpu==1.10.0 \ --hash=sha256:e71f7e24d5b02d3a51df47b77bd10f394a1b48a8331d5c817e71e9e27a8a75ac \ --hash=sha256:f71c5860c860df2320299f9e4f2ca1725beb559c04acb1cf961ed24e6218277a # via feast (pyproject.toml) -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -1239,9 +1244,9 @@ fastjsonschema==2.21.2 \ --hash=sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463 \ --hash=sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de # via nbformat -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via # datasets # huggingface-hub @@ -1406,9 +1411,9 @@ geomet==1.1.0 \ --hash=sha256:4372fe4e286a34acc6f2e9308284850bd8c4aa5bc12065e2abbd4995900db12f \ --hash=sha256:51e92231a0ef6aaa63ac20c443377ba78a303fd2ecd179dc3567de79f3c11605 # via cassandra-driver -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -1419,9 +1424,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-storage # opencensus # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -1902,13 +1907,13 @@ ibis-framework[duckdb, mssql, oracle]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -identify==2.6.18 \ - --hash=sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd \ - --hash=sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737 +identify==2.6.19 \ + --hash=sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a \ + --hash=sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842 # via pre-commit -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1928,17 +1933,17 @@ importlib-metadata==8.7.1 \ --hash=sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb \ --hash=sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151 # via opentelemetry-api -importlib-resources==6.5.2 \ - --hash=sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c \ - --hash=sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec +importlib-resources==7.1.0 \ + --hash=sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708 \ + --hash=sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1 # via happybase iniconfig==2.3.0 \ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 # via pytest -invoke==2.2.1 \ - --hash=sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8 \ - --hash=sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707 +invoke==3.0.3 \ + --hash=sha256:437b6a622223824380bfb4e64f612711a6b648c795f565efc8625af66fb57f0c \ + --hash=sha256:f11327165e5cbb89b2ad1d88d3292b5113332c43b8553b494da435d6ec6f5053 # via paramiko ipykernel==7.2.0 \ --hash=sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e \ @@ -2055,9 +2060,9 @@ jupyter-core==5.9.1 \ # nbclient # nbconvert # nbformat -jupyter-events==0.12.0 \ - --hash=sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb \ - --hash=sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b +jupyter-events==0.12.1 \ + --hash=sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf \ + --hash=sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3 # via jupyter-server jupyter-lsp==2.3.1 \ --hash=sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81 \ @@ -2094,9 +2099,9 @@ jupyterlab-widgets==3.0.16 \ --hash=sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0 \ --hash=sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8 # via ipywidgets -jwcrypto==1.5.6 \ - --hash=sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789 \ - --hash=sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039 +jwcrypto==1.5.7 \ + --hash=sha256:70204d7cca406eda8c82352e3c41ba2d946610dafd19e54403f0a1f4f18633c6 \ + --hash=sha256:729463fefe28b6de5cf1ebfda3e94f1a1b41d2799148ef98a01cb9678ebe2bb0 # via python-keycloak kube-authkit==0.4.0 \ --hash=sha256:1df61ac392fca96c8f5ae8c3d6e9918f1e1655d212434b3c3da5f92cc23b660d \ @@ -2113,9 +2118,9 @@ lark==1.3.1 \ --hash=sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905 \ --hash=sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12 # via rfc3987-syntax -latex2mathml==3.79.0 \ - --hash=sha256:11bde318c2d2d6fcdd105a07509d867cee2208f653278eb80243dec7ea77a0ce \ - --hash=sha256:9f10720d4fcf6b22d1b81f6628237832419a7a29783c13aa92fa8d680165e63d +latex2mathml==3.81.0 \ + --hash=sha256:4b959cdc3cac8686bc0e3e5aece8127dfb1b81ca1241bed8e00ef31b82bb4022 \ + --hash=sha256:d317710393fe20579aea39cfe8928fa2ad9b8780896e585326c75e89c1d1d1a4 # via docling-core lazy-loader==0.5 \ --hash=sha256:717f9179a0dbed357012ddad50a5ad3d5e4d9a0b8712680d4e687f5e6e6ed9b3 \ @@ -2590,9 +2595,9 @@ mpmath==1.3.0 \ --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c # via sympy -msal==1.35.1 \ - --hash=sha256:70cac18ab80a053bff86219ba64cfe3da1f307c74b009e2da57ef040eb1b5656 \ - --hash=sha256:8f4e82f34b10c19e326ec69f44dc6b30171f2f7098f3720ea8a9f0c11832caa3 +msal==1.36.0 \ + --hash=sha256:36ecac30e2ff4322d956029aabce3c82301c29f0acb1ad89b94edcabb0e58ec4 \ + --hash=sha256:3f6a4af2b036b476a4215111c4297b4e6e236ed186cd804faefba23e4990978b # via # azure-identity # msal-extensions @@ -2878,9 +2883,9 @@ nbclient==0.10.4 \ --hash=sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9 \ --hash=sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440 # via nbconvert -nbconvert==7.17.0 \ - --hash=sha256:1b2696f1b5be12309f6c7d707c24af604b87dfaf6d950794c7b07acab96dda78 \ - --hash=sha256:4f99a63b337b9a23504347afdab24a11faa7d86b405e5c8f9881cd313336d518 +nbconvert==7.17.1 \ + --hash=sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2 \ + --hash=sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8 # via jupyter-server nbformat==5.10.4 \ --hash=sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a \ @@ -3057,8 +3062,8 @@ opencv-python-headless==4.13.0.92 \ --hash=sha256:a7cf08e5b191f4ebb530791acc0825a7986e0d0dee2a3c491184bd8599848a4b \ --hash=sha256:eb60e36b237b1ebd40a912da5384b348df8ed534f6f644d8e0b4f103e272ba7d # via easyocr -openlineage-python==1.45.0 \ - --hash=sha256:cf66e7d517d3c8b510b39ad646d8fd0ca2f0cc92d7d6d601d93b2a859783f380 +openlineage-python==1.46.0 \ + --hash=sha256:f6228a01d34990e76ede5b55b3f99169e54e2e624814c4493f064b9cb1bfba37 # via feast (pyproject.toml) openpyxl==3.1.5 \ --hash=sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2 \ @@ -3068,30 +3073,30 @@ openshift-client==1.0.18 \ --hash=sha256:be3979440cfd96788146a3a1650dabe939d4d516eea0b39f87e66d2ab39495b1 \ --hash=sha256:d8a84080307ccd9556f6c62a3707a3e6507baedee36fa425754f67db9ded528b # via codeflare-sdk -opentelemetry-api==1.40.0 \ - --hash=sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f \ - --hash=sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9 +opentelemetry-api==1.41.1 \ + --hash=sha256:0ad1814d73b875f84494387dae86ce0b12c68556331ce6ce8fe789197c949621 \ + --hash=sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f # via # opentelemetry-exporter-prometheus # opentelemetry-sdk # opentelemetry-semantic-conventions -opentelemetry-exporter-prometheus==0.61b0 \ - --hash=sha256:3013b41f4370143d48d219a2351473761423e5882fa4c213811eaefacba39cb7 \ - --hash=sha256:7c4919bd8e79abd62b610767e80f42c9c3a06c5183f4dd9141eedeb57aea284b +opentelemetry-exporter-prometheus==0.62b1 \ + --hash=sha256:7a0b8a6402e107e1f93e38f074a668797e1103936b189561959531a67ffeba55 \ + --hash=sha256:7ecbac9aa76e7abb44082ab0ff2983e0a573e4091c4653f7db483b02bae03506 # via ray opentelemetry-proto==1.27.0 \ --hash=sha256:33c9345d91dafd8a74fc3d7576c5a38f18b7fdf8d02983ac67485386132aedd6 \ --hash=sha256:b133873de5581a50063e1e4b29cdcf0c5e253a8c2d8dc1229add20a4c3830ace # via ray -opentelemetry-sdk==1.40.0 \ - --hash=sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2 \ - --hash=sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1 +opentelemetry-sdk==1.41.1 \ + --hash=sha256:724b615e1215b5aeacda0abb8a6a8922c9a1853068948bd0bd225a56d0c792e6 \ + --hash=sha256:edee379c126c1bce952b0c812b48fe8ff35b30df0eecf17e98afa4d598b7d85d # via # opentelemetry-exporter-prometheus # ray -opentelemetry-semantic-conventions==0.61b0 \ - --hash=sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a \ - --hash=sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2 +opentelemetry-semantic-conventions==0.62b1 \ + --hash=sha256:c5cc6e04a7f8c7cdd30be2ed81499fa4e75bfbd52c9cb70d40af1f9cd3619802 \ + --hash=sha256:cf506938103d331fbb78eded0d9788095f7fd59016f2bda813c3324e5a74a93c # via opentelemetry-sdk oracledb==3.4.2 \ --hash=sha256:00c79448017f367bb7ab6900efe0706658a53768abea2b4519a4c9b2d5743890 \ @@ -3202,9 +3207,9 @@ orjson==3.11.8 \ --hash=sha256:fe0b8c83e0f36247fc9431ce5425a5d95f9b3a689133d494831bdbd6f0bceb13 \ --hash=sha256:ff51f9d657d1afb6f410cb435792ce4e1fe427aab23d2fcd727a2876e21d4cb6 # via trino -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # accelerate # build @@ -3495,9 +3500,9 @@ pre-commit==3.3.1 \ --hash=sha256:218e9e3f7f7f3271ebc355a15598a4d3893ad9fc7b57fe446db75644543323b9 \ --hash=sha256:733f78c9a056cdd169baa6cd4272d51ecfda95346ef8a89bf93712706021b907 # via feast (pyproject.toml) -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via # feast (pyproject.toml) # jupyter-server @@ -3816,57 +3821,57 @@ py4j==0.10.9.9 \ --hash=sha256:c7c26e4158defb37b0bb124933163641a2ff6e3a3913f7811b0ddbe07ed61533 \ --hash=sha256:f694cad19efa5bd1dee4f3e5270eb406613c974394035e5bfc4ec1aba870b879 # via pyspark -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -3979,9 +3984,9 @@ pycryptodome==3.23.0 \ --hash=sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be \ --hash=sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7 # via minio -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # codeflare-sdk @@ -3997,134 +4002,134 @@ pydantic==2.12.5 \ # pydantic-settings # qdrant-client # ray -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # docling + # docling-core # fastapi-mcp # mcp pydata-google-auth==1.9.1 \ @@ -4159,6 +4164,79 @@ pymilvus==2.5.18 \ --hash=sha256:1b78badcfa8d62db7d0b29193fc0422e4676873ff1c745a9d75c2c885d7a7e32 \ --hash=sha256:9e517076068e98dac51c018bc0dfe1f651d936154e2e2d9ad6c7b3dab1164e2d # via feast (pyproject.toml) +pymongo==4.17.0 \ + --hash=sha256:0ff6bd2f735ab5356541e3e57d5b7dbfbc3f2ee1ccb10b6b0f82d58af69d1d8e \ + --hash=sha256:1175563375d682260f613a96fb7a53dce746ed752bfd924eab61de3bc5bfde34 \ + --hash=sha256:1195370a77baf003b59b10e91ecc4706297197f0dd9d29c840cc556dc08f7cee \ + --hash=sha256:12c4fded3a9f1d6a687e36ebd384ac6d00b9b00de1969aa74048e7051ec2a713 \ + --hash=sha256:15d3f3d732aecac1f8d481bde4029755615639bd3076f258a2147210aec8515a \ + --hash=sha256:20323b0b1c1d33770ad1fc68d429c757734ce9ad3594421c3d6618f10572b1b9 \ + --hash=sha256:2a0d5ac205728c86e0a02192f1aa5f865b0d7d51f8df6101c01a69a7fc620d72 \ + --hash=sha256:2db66aa8dd253a0fc1fad3b0d23d5b3993f7ebde02fbbd7727128debf2853675 \ + --hash=sha256:2e190827834fce70ecdf9d46796c6dbc0ce08ea87dc2ff5bc6f3f5579b605cb9 \ + --hash=sha256:320b34457b20bbcc79997801f95d25ce00472915ca5241167242b42c4359e027 \ + --hash=sha256:3689ea34f6b647c7d1e7bdc60fcfb214b2789ed1359a7fb96569c69f50e5f18f \ + --hash=sha256:37a8385c29881b43eab31f584100fa0eaddedd5607adf010147ba1810118be90 \ + --hash=sha256:3987e96e7c7be4083d42e8ac2cc6c0d5b78db9973c90fce42ae800b616ca6b20 \ + --hash=sha256:4141e6c6a339789b2974efa00ecd9409101672d77a0e3ee2cc3839eedf8ec4df \ + --hash=sha256:422fa50d7d7f5c22ea0953554396c9ef95684a2d775f860bd75a7b510538dfca \ + --hash=sha256:47b021363cd923ace5edc7a1d63c0ff8a6d9d43859b8a1ba23645f5afae63221 \ + --hash=sha256:485c8a8eaa4c739f00a331fc73757898ee7c092c214a79e63866ff76aaf282ff \ + --hash=sha256:48bbc576677b50af043df870d84ded67cc3a9b4aa7553201beef4da5dc050a0a \ + --hash=sha256:4ae22fafca69dd3c78261969e999782ac5fc23b76cf8cccfbc3707982a74cc3d \ + --hash=sha256:50e8f8e23c6df7c6d6929f5e734980b227706e73ee847517c9ba5af90f7fc466 \ + --hash=sha256:51e1915761f65f2aaabd0ba691a31d56551d3f19d1263c2d6bf261730603de5f \ + --hash=sha256:5376ad67bb30ae910d83affcf997f706d9dee37e8b5dad8b6fedb0626e262d85 \ + --hash=sha256:5960519b4d7168f1ecdd3ea10c81b2aedeb9423651aca953cfbc8e76705d3b38 \ + --hash=sha256:5a5de048e6da5c18e27cc2437e8c15b3b0cdc8385c15b41178b0caa3322a09c2 \ + --hash=sha256:5ab3b8ff79e0dfc49b68f3c925e8cc735ea95c60efaed84cfe75692dffcaac2a \ + --hash=sha256:64837adbbd72073301af51bb0fc80e3d7707fe5527cea1033ba0320f0b2f881b \ + --hash=sha256:6877214bff5f06f6884a9fc8d9016a4a7a5f51f537f5c51ac3a576f93e7dfb32 \ + --hash=sha256:68fca71e05ee5da23a8d73cee8379dfb3d26e609a377cae731d742771ed96946 \ + --hash=sha256:6c5f62862d0f87be481fa1fe8cb811994486773c94a2b61e509285e3f2890763 \ + --hash=sha256:6fe0de9d0f6791abce3471230b32b4817bf89d27b1182b6a550e1ec0fa72aa9a \ + --hash=sha256:70ffa08ba641468cc068cf46c06b34f01a8ce3489f6411309fcb5ceabe6b2fc0 \ + --hash=sha256:757f2a4c0c2c46cab87df0333681ce69e86c9d5b45bc5203ceba5410b3489e59 \ + --hash=sha256:75bc3aa5b94fdb7138d357ec6ca61cd97e0c79f4f7f0bd3efe9639b15cc50942 \ + --hash=sha256:77aa4bc164b4de60d5db193b322f0f5b6ead716e831031bfdef8e8bd92205556 \ + --hash=sha256:7db10678814cdf7ea39fd308c6f41395cfa7b29d904bcd7895288963d8f892ba \ + --hash=sha256:809ec74de3b9148ae43fa8df9faf53470f511c8d384f13b99d6f671f2a379f15 \ + --hash=sha256:8446ff4bfcb6ec2a2e50998c860986a1e992136f998b7f53e7a717fb8aa5a0b9 \ + --hash=sha256:8a1be016198a03fd7727cdd55998964bfa4e5a6fd9733c8e95830628cef34d29 \ + --hash=sha256:8e97e03fa13327c87e3fdc5656acd01e71817f0c1dc3221cd8f30de136bf4ec3 \ + --hash=sha256:93641192644fa1ee0f34030e774fd31022a27ad11ba22cb1716142231524f8bd \ + --hash=sha256:9543d8f84c2e5608565c08ac679774811e6730770d8a645439b073422a4276fb \ + --hash=sha256:9828485f72f63c7d802e0ec41f71906f633c2692621ab3af55ca990186b091b1 \ + --hash=sha256:9eb5d63a3c518cb0804ed678f5e2b875af032d89a7cf57a57360322cf6a4d222 \ + --hash=sha256:a431b737816bf4cddd4fa0fcef04e424ad36b7692734a64150f872fb8f3208be \ + --hash=sha256:a8f9c40a09bb7d4b9fc8b1da65ecf6efa79bda5cb2756f39d9b6940fac1d19ae \ + --hash=sha256:addd0498ebbdc6354227f6ed457ed9fce442d48a3bb30d5b5bad33e104996561 \ + --hash=sha256:b24598dc3c2feccbc83b43044be48145a0dc4f9bee49ef923e3d707d54a55d85 \ + --hash=sha256:b2dfcc795f5b9fedbe179a11fdf6051581479d196582a3fe819a92a00e9b9969 \ + --hash=sha256:b4384700cffc3f1dd98e088bc0072dedf6d7d68a230bb4b972665cf69c071c1e \ + --hash=sha256:b93b22eedc62598cf5ee9d8c8007a8e9121c50fd88137012d8985500e9dc3151 \ + --hash=sha256:ba2195d4f386f839a52a23ea1cfd60ffaaba78a3d7841db51b7e433001139918 \ + --hash=sha256:bb3ebc86782049f6928dcc583008287cb1c17d463501c94a620f035f5b4fd463 \ + --hash=sha256:bd835cdb37a1adec359dd072c24f8bb14809e2644fde86fab4ee2fc9719b9483 \ + --hash=sha256:c2292144505fb12156b981bd440f3dc994a883da06ac726c0c8692ccdbc1c510 \ + --hash=sha256:c4979e7e8887862bbb44d203f00cc8263a3f27237876fa691b6beba23e40e6d8 \ + --hash=sha256:c5c8e180cb2cabe37300e1e36c60aa4f2ff956cc579f0142135a5d2cba252243 \ + --hash=sha256:c797f8a80957134f6dd9690367a0f8f5906d672119af2c6aa55f0c527b656bed \ + --hash=sha256:c9786665926a09630c5d420c79762cfadbff35a9438bcbc4c81a9fb5ab9228b7 \ + --hash=sha256:cee36b3c0d0354f880fa7a7fdcdaf2bb5e542c2281e25c1bfadf8cfe21eba7d2 \ + --hash=sha256:d53ffa94b2340dbf6b055e09a0090618c60482c158ecfc9565642fc996bf0944 \ + --hash=sha256:df4a644af9ae132d4bfdb2e9516ea51a615fd881caddfbfbd071cf1354844479 \ + --hash=sha256:dff3de1294fbbc1db0ba6b511f77b8e540601d092538a31312e99c8a91a78b1e \ + --hash=sha256:e46767f28dea610e02edf6c5d956ce615c3c7790ea396660b9b1efd5c5ead2e0 \ + --hash=sha256:e4fab10f8403169ce92f3cea921609d9ee81107306caae06c08f592d4b8ad2b5 \ + --hash=sha256:e537e95514dae1aaa718f481ec03151a0f0394bcd05f1322896d8fc1330cb729 \ + --hash=sha256:e68c76b84e0c132d9dbf9307f12ff8185702328187a87b9aca8c941303873433 \ + --hash=sha256:e816db649ba5d7de0568cf3a9f287a9dc9aad21cf0ca667ab156a7ef47fca0b0 \ + --hash=sha256:f09645e0ce4e3825fa0baa8254064a716ed0be33f78feeedd4731016cb8aaa17 \ + --hash=sha256:f3ee3d241ed77a4fc99ce3cff3b289c3ebce37f61fdd7349d3592c23b82c8784 \ + --hash=sha256:faf03e4c2aafd6de626dbd30ba246d369ae33f47f10629d1bbe40f72115027a6 \ + --hash=sha256:ff5aa3f1c7e3f08eb0e7a016c91ba468b1850ccfd63d9b1f12f56350f4974cef + # via feast (pyproject.toml) pymssql==2.3.2 \ --hash=sha256:06883bc9bdb297ae9132d9371b5b1a3a223c8f93dd6a87d1c112c6a688f26d53 \ --hash=sha256:0768d90f96ae3267d7561d3bcfe94dd671d107489e870388b12570c3debbc552 \ @@ -4318,9 +4396,9 @@ pyodbc==5.3.0 \ # via # feast (pyproject.toml) # ibis-framework -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python pyparsing==3.3.2 \ --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ @@ -4546,9 +4624,9 @@ python-keycloak==4.2.2 \ --hash=sha256:1d43a1accd4a038ed39317fcb3eb78211df6c75bbcbc4c482c99ee76327136f2 \ --hash=sha256:5137fd87c69031a372a578df96bae96b9aead2c9dad976613bc978e9e0246a1e # via feast (pyproject.toml) -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp python-pptx==1.0.2 \ --hash=sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba \ @@ -4784,121 +4862,121 @@ referencing==0.37.0 \ # jsonschema # jsonschema-specifications # jupyter-events -regex==2026.3.32 \ - --hash=sha256:03c2ebd15ff51e7b13bb3dc28dd5ac18cd39e59ebb40430b14ae1a19e833cff1 \ - --hash=sha256:09e26cad1544d856da85881ad292797289e4406338afe98163f3db9f7fac816c \ - --hash=sha256:0cec365d44835b043d7b3266487797639d07d621bec9dc0ea224b00775797cc1 \ - --hash=sha256:0d7855f5e59fcf91d0c9f4a51dc5d8847813832a2230c3e8e35912ccf20baaa2 \ - --hash=sha256:0f21ae18dfd15752cdd98d03cbd7a3640be826bfd58482a93f730dbd24d7b9fb \ - --hash=sha256:10fb2aaae1aaadf7d43c9f3c2450404253697bf8b9ce360bd5418d1d16292298 \ - --hash=sha256:110ba4920721374d16c4c8ea7ce27b09546d43e16aea1d7f43681b5b8f80ba61 \ - --hash=sha256:12917c6c6813ffcdfb11680a04e4d63c5532b88cf089f844721c5f41f41a63ad \ - --hash=sha256:18eb45f711e942c27dbed4109830bd070d8d618e008d0db39705f3f57070a4c6 \ - --hash=sha256:1a6ac1ed758902e664e0d95c1ee5991aa6fb355423f378ed184c6ec47a1ec0e9 \ - --hash=sha256:1ca02ff0ef33e9d8276a1fcd6d90ff6ea055a32c9149c0050b5b67e26c6d2c51 \ - --hash=sha256:1cb22fa9ee6a0acb22fc9aecce5f9995fe4d2426ed849357d499d62608fbd7f9 \ - --hash=sha256:1e0f6648fd48f4c73d801c55ab976cd602e2da87de99c07bff005b131f269c6a \ - --hash=sha256:245667ad430745bae6a1e41081872d25819d86fbd9e0eec485ba00d9f78ad43d \ - --hash=sha256:2820d2231885e97aff0fcf230a19ebd5d2b5b8a1ba338c20deb34f16db1c7897 \ - --hash=sha256:2c8d402ea3dfe674288fe3962016affd33b5b27213d2b5db1823ffa4de524c57 \ - --hash=sha256:2dcca2bceb823c9cc610e57b86a265d7ffc30e9fe98548c609eba8bd3c0c2488 \ - --hash=sha256:2ffbadc647325dd4e3118269bda93ded1eb5f5b0c3b7ba79a3da9fbd04f248e9 \ - --hash=sha256:34c905a721ddee0f84c99e3e3b59dd4a5564a6fe338222bc89dd4d4df166115c \ - --hash=sha256:3c054e39a9f85a3d76c62a1d50c626c5e9306964eaa675c53f61ff7ec1204bbb \ - --hash=sha256:3c0bbfbd38506e1ea96a85da6782577f06239cb9fcf9696f1ea537c980c0680b \ - --hash=sha256:3e221b615f83b15887636fcb90ed21f1a19541366f8b7ba14ba1ad8304f4ded4 \ - --hash=sha256:3ea568832eca219c2be1721afa073c1c9eb8f98a9733fdedd0a9747639fc22a5 \ - --hash=sha256:3f5747501b69299c6b0b047853771e4ed390510bada68cb16da9c9c2078343f7 \ - --hash=sha256:462a041d2160090553572f6bb0be417ab9bb912a08de54cb692829c871ee88c1 \ - --hash=sha256:4bc32b4dbdb4f9f300cf9f38f8ea2ce9511a068ffaa45ac1373ee7a943f1d810 \ - --hash=sha256:4d082be64e51671dd5ee1c208c92da2ddda0f2f20d8ef387e57634f7e97b6aae \ - --hash=sha256:4f9ae4755fa90f1dc2d0d393d572ebc134c0fe30fcfc0ab7e67c1db15f192041 \ - --hash=sha256:51a93452034d671b0e21b883d48ea66c5d6a05620ee16a9d3f229e828568f3f0 \ - --hash=sha256:51fb7e26f91f9091fd8ec6a946f99b15d3bc3667cb5ddc73dd6cb2222dd4a1cc \ - --hash=sha256:5336b1506142eb0f23c96fb4a34b37c4fefd4fed2a7042069f3c8058efe17855 \ - --hash=sha256:567b57eb987547a23306444e4f6f85d4314f83e65c71d320d898aa7550550443 \ - --hash=sha256:5aa78c857c1731bdd9863923ffadc816d823edf475c7db6d230c28b53b7bdb5e \ - --hash=sha256:5bf2f3c2c5bd8360d335c7dcd4a9006cf1dabae063ee2558ee1b07bbc8a20d88 \ - --hash=sha256:5c35d097f509cf7e40d20d5bee548d35d6049b36eb9965e8d43e4659923405b9 \ - --hash=sha256:5d86e3fb08c94f084a625c8dc2132a79a3a111c8bf6e2bc59351fa61753c2f6e \ - --hash=sha256:6062c4ef581a3e9e503dccf4e1b7f2d33fdc1c13ad510b287741ac73bc4c6b27 \ - --hash=sha256:6128dd0793a87287ea1d8bf16b4250dd96316c464ee15953d5b98875a284d41e \ - --hash=sha256:631f7d95c83f42bccfe18946a38ad27ff6b6717fb4807e60cf24860b5eb277fc \ - --hash=sha256:66a5083c3ffe5a5a95f8281ea47a88072d4f24001d562d1d9d28d4cdc005fec5 \ - --hash=sha256:66d3126afe7eac41759cd5f0b3b246598086e88e70527c0d68c9e615b81771c4 \ - --hash=sha256:67015a8162d413af9e3309d9a24e385816666fbf09e48e3ec43342c8536f7df6 \ - --hash=sha256:6980ceb5c1049d4878632f08ba0bf7234c30e741b0dc9081da0f86eca13189d3 \ - --hash=sha256:69a847a6ffaa86e8af7b9e7037606e05a6f663deec516ad851e8e05d9908d16a \ - --hash=sha256:6ada7bd5bb6511d12177a7b00416ce55caee49fbf8c268f26b909497b534cacb \ - --hash=sha256:70c634e39c5cda0da05c93d6747fdc957599f7743543662b6dbabdd8d3ba8a96 \ - --hash=sha256:7cdd508664430dd51b8888deb6c5b416d8de046b2e11837254378d31febe4a98 \ - --hash=sha256:844d88509c968dd44b30daeefac72b038b1bf31ac372d5106358ab01d393c48b \ - --hash=sha256:847087abe98b3c1ebf1eb49d6ef320dbba75a83ee4f83c94704580f1df007dd4 \ - --hash=sha256:85c9b0c131427470a6423baa0a9330be6fd8c3630cc3ee6fdee03360724cbec5 \ - --hash=sha256:879ae91f2928a13f01a55cfa168acedd2b02b11b4cd8b5bb9223e8cde777ca52 \ - --hash=sha256:887a9fa74418d74d645281ee0edcf60694053bd1bc2ebc49eb5e66bfffc6d107 \ - --hash=sha256:88ebc0783907468f17fca3d7821b30f9c21865a721144eb498cb0ff99a67bcac \ - --hash=sha256:89e50667e7e8c0e7903e4d644a2764fffe9a3a5d6578f72ab7a7b4205bf204b7 \ - --hash=sha256:8a4a3189a99ecdd1c13f42513ab3fc7fa8311b38ba7596dd98537acb8cd9acc3 \ - --hash=sha256:8aaf8ee8f34b677f90742ca089b9c83d64bdc410528767273c816a863ed57327 \ - --hash=sha256:8e4c8fa46aad1a11ae2f8fcd1c90b9d55e18925829ac0d98c5bb107f93351745 \ - --hash=sha256:8fc918cd003ba0d066bf0003deb05a259baaaab4dc9bd4f1207bbbe64224857a \ - --hash=sha256:8fe14e24124ef41220e5992a0f09432f890037df6f93fd3d6b7a0feff2db16b2 \ - --hash=sha256:918db4e34a7ef3d0beee913fa54b34231cc3424676f1c19bdb85f01828d3cd37 \ - --hash=sha256:987cdfcfb97a249abc3601ad53c7de5c370529f1981e4c8c46793e4a1e1bfe8e \ - --hash=sha256:9b9118a78e031a2e4709cd2fcc3028432e89b718db70073a8da574c249b5b249 \ - --hash=sha256:9cf7036dfa2370ccc8651521fcbb40391974841119e9982fa312b552929e6c85 \ - --hash=sha256:a094e9dcafedfb9d333db5cf880304946683f43a6582bb86688f123335122929 \ - --hash=sha256:a416ee898ecbc5d8b283223b4cf4d560f93244f6f7615c1bd67359744b00c166 \ - --hash=sha256:a5d88fa37ba5e8a80ca8d956b9ea03805cfa460223ac94b7d4854ee5e30f3173 \ - --hash=sha256:ace48c5e157c1e58b7de633c5e257285ce85e567ac500c833349c363b3df69d4 \ - --hash=sha256:ad5c53f2e8fcae9144009435ebe3d9832003508cf8935c04542a1b3b8deefa15 \ - --hash=sha256:ad8d372587e659940568afd009afeb72be939c769c552c9b28773d0337251391 \ - --hash=sha256:b193ed199848aa96618cd5959c1582a0bf23cd698b0b900cb0ffe81b02c8659c \ - --hash=sha256:b2e9c2ea2e93223579308263f359eab8837dc340530b860cb59b713651889f14 \ - --hash=sha256:b3aa21bad31db904e0b9055e12c8282df62d43169c4a9d2929407060066ebc74 \ - --hash=sha256:b565f25171e04d4fad950d1fa837133e3af6ea6f509d96166eed745eb0cf63bc \ - --hash=sha256:b56993a7aeb4140c4770f4f7965c9e5af4f024457d06e23c01b0d47501cb18ed \ - --hash=sha256:b6acb765e7c1f2fa08ac9057a33595e26104d7d67046becae184a8f100932dd9 \ - --hash=sha256:b6f366a5ef66a2df4d9e68035cfe9f0eb8473cdfb922c37fac1d169b468607b0 \ - --hash=sha256:b7836aa13721dbdef658aebd11f60d00de633a95726521860fe1f6be75fa225a \ - --hash=sha256:b8fca73e16c49dd972ce3a88278dfa5b93bf91ddef332a46e9443abe21ca2f7c \ - --hash=sha256:b953d9d496d19786f4d46e6ba4b386c6e493e81e40f9c5392332458183b0599d \ - --hash=sha256:bbc458a292aee57d572075f22c035fa32969cdb7987d454e3e34d45a40a0a8b4 \ - --hash=sha256:c1cecea3e477af105f32ef2119b8d895f297492e41d317e60d474bc4bffd62ff \ - --hash=sha256:c1d7fa44aece1fa02b8927441614c96520253a5cad6a96994e3a81e060feed55 \ - --hash=sha256:c1ed17104d1be7f807fdec35ec99777168dd793a09510d753f8710590ba54cdd \ - --hash=sha256:c3c6f6b027d10f84bfe65049028892b5740878edd9eae5fea0d1710b09b1d257 \ - --hash=sha256:c5e0fdb5744caf1036dec5510f543164f2144cb64932251f6dfd42fa872b7f9c \ - --hash=sha256:c60f1de066eb5a0fd8ee5974de4194bb1c2e7692941458807162ffbc39887303 \ - --hash=sha256:c6d9c6e783b348f719b6118bb3f187b2e138e3112576c9679eb458cc8b2e164b \ - --hash=sha256:c940e00e8d3d10932c929d4b8657c2ea47d2560f31874c3e174c0d3488e8b865 \ - --hash=sha256:c9f261ad3cd97257dc1d9355bfbaa7dd703e06574bffa0fa8fe1e31da915ee38 \ - --hash=sha256:d21a07edddb3e0ca12a8b8712abc8452481c3d3db19ae87fc94e9842d005964b \ - --hash=sha256:d363660f9ef8c734495598d2f3e527fb41f745c73159dc0d743402f049fb6836 \ - --hash=sha256:d478a2ca902b6ef28ffc9521e5f0f728d036abe35c0b250ee8ae78cfe7c5e44e \ - --hash=sha256:d571f0b2eec3513734ea31a16ce0f7840c0b85a98e7edfa0e328ed144f9ef78f \ - --hash=sha256:d6b39a2cc5625bbc4fda18919a891eab9aab934eecf83660a90ce20c53621a9a \ - --hash=sha256:d76d62909bfb14521c3f7cfd5b94c0c75ec94b0a11f647d2f604998962ec7b6c \ - --hash=sha256:dab4178a0bc1ef13178832b12db7bc7f562e8f028b2b5be186e370090dc50652 \ - --hash=sha256:db976be51375bca900e008941639448d148c655c9545071965d0571ecc04f5d0 \ - --hash=sha256:ded4fc0edf3de792850cb8b04bbf3c5bd725eeaf9df4c27aad510f6eed9c4e19 \ - --hash=sha256:e006ea703d5c0f3d112b51ba18af73b58209b954acfe3d8da42eacc9a00e4be6 \ - --hash=sha256:e3e5d1802cba785210a4a800e63fcee7a228649a880f3bf7f2aadccb151a834b \ - --hash=sha256:e480d3dac06c89bc2e0fd87524cc38c546ac8b4a38177650745e64acbbcfdeba \ - --hash=sha256:e50af656c15e2723eeb7279c0837e07accc594b95ec18b86821a4d44b51b24bf \ - --hash=sha256:e83ce8008b48762be296f1401f19afd9ea29f3d035d1974e0cecb74e9afbd1df \ - --hash=sha256:ed3b8281c5d0944d939c82db4ec2300409dd69ee087f7a75a94f2e301e855fb4 \ - --hash=sha256:ef250a3f5e93182193f5c927c5e9575b2cb14b80d03e258bc0b89cc5de076b60 \ - --hash=sha256:f1574566457161678297a116fa5d1556c5a4159d64c5ff7c760e7c564bf66f16 \ - --hash=sha256:f26262900edd16272b6360014495e8d68379c6c6e95983f9b7b322dc928a1194 \ - --hash=sha256:f28eac18a8733a124444643a66ac96fef2c0ad65f50034e0a043b90333dc677f \ - --hash=sha256:f54840bea73541652f1170dc63402a5b776fc851ad36a842da9e5163c1f504a0 \ - --hash=sha256:f785f44a44702dea89b28bce5bc82552490694ce4e144e21a4f0545e364d2150 \ - --hash=sha256:f7cc00089b4c21847852c0ad76fb3680f9833b855a0d30bcec94211c435bff6b \ - --hash=sha256:f95bd07f301135771559101c060f558e2cf896c7df00bec050ca7f93bf11585a \ - --hash=sha256:fc8ced733d6cd9af5e412f256a32f7c61cd2d7371280a65c689939ac4572499f \ - --hash=sha256:fd03e38068faeef937cc6761a250a4aaa015564bd0d61481fefcf15586d31825 +regex==2026.4.4 \ + --hash=sha256:011bb48bffc1b46553ac704c975b3348717f4e4aa7a67522b51906f99da1820c \ + --hash=sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f \ + --hash=sha256:0540e5b733618a2f84e9cb3e812c8afa82e151ca8e19cf6c4e95c5a65198236f \ + --hash=sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62 \ + --hash=sha256:0709f22a56798457ae317bcce42aacee33c680068a8f14097430d9f9ba364bee \ + --hash=sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883 \ + --hash=sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13 \ + --hash=sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99 \ + --hash=sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a \ + --hash=sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0 \ + --hash=sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566 \ + --hash=sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9 \ + --hash=sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76 \ + --hash=sha256:1b9a00b83f3a40e09859c78920571dcb83293c8004079653dd22ec14bbfa98c7 \ + --hash=sha256:21e5eb86179b4c67b5759d452ea7c48eb135cd93308e7a260aa489ed2eb423a4 \ + --hash=sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717 \ + --hash=sha256:2895506ebe32cc63eeed8f80e6eae453171cfccccab35b70dc3129abec35a5b8 \ + --hash=sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17 \ + --hash=sha256:2a5d273181b560ef8397c8825f2b9d57013de744da9e8257b8467e5da8599351 \ + --hash=sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d \ + --hash=sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb \ + --hash=sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7 \ + --hash=sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8 \ + --hash=sha256:312ec9dd1ae7d96abd8c5a36a552b2139931914407d26fba723f9e53c8186f86 \ + --hash=sha256:33424f5188a7db12958246a54f59a435b6cb62c5cf9c8d71f7cc49475a5fdada \ + --hash=sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81 \ + --hash=sha256:33bfda9684646d323414df7abe5692c61d297dbb0530b28ec66442e768813c59 \ + --hash=sha256:349d7310eddff40429a099c08d995c6d4a4bfaf3ff40bd3b5e5cb5a5a3c7d453 \ + --hash=sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141 \ + --hash=sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031 \ + --hash=sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74 \ + --hash=sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244 \ + --hash=sha256:415a994b536440f5011aa77e50a4274d15da3245e876e5c7f19da349caaedd87 \ + --hash=sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f \ + --hash=sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465 \ + --hash=sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983 \ + --hash=sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff \ + --hash=sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0 \ + --hash=sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55 \ + --hash=sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752 \ + --hash=sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73 \ + --hash=sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe \ + --hash=sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95 \ + --hash=sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8 \ + --hash=sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb \ + --hash=sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45 \ + --hash=sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943 \ + --hash=sha256:6780f008ee81381c737634e75c24e5a6569cc883c4f8e37a37917ee79efcafd9 \ + --hash=sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520 \ + --hash=sha256:6aa809ed4dc3706cc38594d67e641601bd2f36d5555b2780ff074edfcb136cf8 \ + --hash=sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1 \ + --hash=sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3 \ + --hash=sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1 \ + --hash=sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb \ + --hash=sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6 \ + --hash=sha256:74fa82dcc8143386c7c0392e18032009d1db715c25f4ba22d23dc2e04d02a20f \ + --hash=sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be \ + --hash=sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4 \ + --hash=sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951 \ + --hash=sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27 \ + --hash=sha256:7d346fccdde28abba117cc9edc696b9518c3307fbfcb689e549d9b5979018c6d \ + --hash=sha256:8512fcdb43f1bf18582698a478b5ab73f9c1667a5b7548761329ef410cd0a760 \ + --hash=sha256:867bddc63109a0276f5a31999e4c8e0eb7bbbad7d6166e28d969a2c1afeb97f9 \ + --hash=sha256:88e9b048345c613f253bea4645b2fe7e579782b82cac99b1daad81e29cc2ed8e \ + --hash=sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7 \ + --hash=sha256:9542ccc1e689e752594309444081582f7be2fdb2df75acafea8a075108566735 \ + --hash=sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81 \ + --hash=sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3 \ + --hash=sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9 \ + --hash=sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790 \ + --hash=sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043 \ + --hash=sha256:a0d2b28aa1354c7cd7f71b7658c4326f7facac106edd7f40eda984424229fd59 \ + --hash=sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a \ + --hash=sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4 \ + --hash=sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f \ + --hash=sha256:a85b620a388d6c9caa12189233109e236b3da3deffe4ff11b84ae84e218a274f \ + --hash=sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427 \ + --hash=sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae \ + --hash=sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa \ + --hash=sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d \ + --hash=sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0 \ + --hash=sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc \ + --hash=sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863 \ + --hash=sha256:b4c36a85b00fadb85db9d9e90144af0a980e1a3d2ef9cd0f8a5bef88054657c6 \ + --hash=sha256:b5f9fb784824a042be3455b53d0b112655686fdb7a91f88f095f3fee1e2a2a54 \ + --hash=sha256:be061028481186ba62a0f4c5f1cc1e3d5ab8bce70c89236ebe01023883bc903b \ + --hash=sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52 \ + --hash=sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07 \ + --hash=sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b \ + --hash=sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b \ + --hash=sha256:cf9b1b2e692d4877880388934ac746c99552ce6bf40792a767fd42c8c99f136d \ + --hash=sha256:d2228c02b368d69b724c36e96d3d1da721561fb9cc7faa373d7bf65e07d75cb5 \ + --hash=sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf \ + --hash=sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b \ + --hash=sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359 \ + --hash=sha256:dcb5453ecf9cd58b562967badd1edbf092b0588a3af9e32ee3d05c985077ce87 \ + --hash=sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca \ + --hash=sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa \ + --hash=sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423 \ + --hash=sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4 \ + --hash=sha256:e355be718caf838aa089870259cf1776dc2a4aa980514af9d02c59544d9a8b22 \ + --hash=sha256:e7ab63e9fe45a9ec3417509e18116b367e89c9ceb6219222a3396fa30b147f80 \ + --hash=sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f \ + --hash=sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17 \ + --hash=sha256:eb59c65069498dbae3c0ef07bbe224e1eaa079825a437fb47a479f0af11f774f \ + --hash=sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e \ + --hash=sha256:ee9627de8587c1a22201cb16d0296ab92b4df5cdcb5349f4e9744d61db7c7c98 \ + --hash=sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4 \ + --hash=sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d \ + --hash=sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b \ + --hash=sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c \ + --hash=sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83 \ + --hash=sha256:fe896e07a5a2462308297e515c0054e9ec2dd18dfdc9427b19900b37dfe6f40b \ + --hash=sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e # via # feast (pyproject.toml) # parsimonious @@ -4964,9 +5042,9 @@ rfc3987-syntax==1.1.0 \ --hash=sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f \ --hash=sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d # via jsonschema -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==14.3.4 \ + --hash=sha256:07e7adb4690f68864777b1450859253bed81a99a31ac321ac1817b2313558952 \ + --hash=sha256:817e02727f2b25b40ef56f5aa2217f400c8489f79ca8f46ea2b70dd5e14558a9 # via # codeflare-sdk # fastapi-mcp @@ -5108,25 +5186,25 @@ ruamel-yaml==0.17.17 \ --hash=sha256:9751de4cbb57d4bfbf8fc394e125ed4a2f170fbff3dc3d78abf50be85924f8be \ --hash=sha256:9af3ec5d7f8065582f3aa841305465025d0afd26c5fb54e15b964e11838fc74f # via great-expectations -ruff==0.15.9 \ - --hash=sha256:058d8e99e1bfe79d8a0def0b481c56059ee6716214f7e425d8e737e412d69677 \ - --hash=sha256:0694e601c028fd97dc5c6ee244675bc241aeefced7ef80cd9c6935a871078f53 \ - --hash=sha256:29cbb1255a9797903f6dde5ba0188c707907ff44a9006eb273b5a17bfa0739a2 \ - --hash=sha256:2b0c7c341f68adb01c488c3b7d4b49aa8ea97409eae6462d860a79cf55f431b6 \ - --hash=sha256:45a70921b80e1c10cf0b734ef09421f71b5aa11d27404edc89d7e8a69505e43d \ - --hash=sha256:4965bac6ac9ea86772f4e23587746f0b7a395eccabb823eb8bfacc3fa06069f7 \ - --hash=sha256:55cc15eee27dc0eebdfcb0d185a6153420efbedc15eb1d38fe5e685657b0f840 \ - --hash=sha256:6d3fcbca7388b066139c523bda744c822258ebdcfbba7d24410c3f454cc9af71 \ - --hash=sha256:6efbe303983441c51975c243e26dff328aca11f94b70992f35b093c2e71801e1 \ - --hash=sha256:7b34a9766aeec27a222373d0b055722900fbc0582b24f39661aa96f3fe6ad901 \ - --hash=sha256:89dd695bc72ae76ff484ae54b7e8b0f6b50f49046e198355e44ea656e521fef9 \ - --hash=sha256:8e1ddb11dbd61d5983fa2d7d6370ef3eb210951e443cace19594c01c72abab4c \ - --hash=sha256:9439a342adb8725f32f92732e2bafb6d5246bd7a5021101166b223d312e8fc59 \ - --hash=sha256:9c5e6faf9d97c8edc43877c3f406f47446fc48c40e1442d58cfcdaba2acea745 \ - --hash=sha256:a6537f6eed5cda688c81073d46ffdfb962a5f29ecb6f7e770b2dc920598997ed \ - --hash=sha256:bde6ff36eaf72b700f32b7196088970bf8fdb2b917b7accd8c371bfc0fd573ec \ - --hash=sha256:ce187224ef1de1bd225bc9a152ac7102a6171107f026e81f317e4257052916d5 \ - --hash=sha256:eaf05aad70ca5b5a0a4b0e080df3a6b699803916d88f006efd1f5b46302daab8 +ruff==0.15.12 \ + --hash=sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b \ + --hash=sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33 \ + --hash=sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0 \ + --hash=sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002 \ + --hash=sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339 \ + --hash=sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e \ + --hash=sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847 \ + --hash=sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f \ + --hash=sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6 \ + --hash=sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d \ + --hash=sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20 \ + --hash=sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd \ + --hash=sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c \ + --hash=sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5 \ + --hash=sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6 \ + --hash=sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c \ + --hash=sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5 \ + --hash=sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5 # via feast (pyproject.toml) s3transfer==0.13.1 \ --hash=sha256:a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724 \ @@ -5330,9 +5408,9 @@ send2trash==2.1.0 \ --hash=sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c \ --hash=sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459 # via jupyter-server -sentence-transformers==5.3.0 \ - --hash=sha256:414a0a881f53a4df0e6cbace75f823bfcb6b94d674c42a384b498959b7c065e2 \ - --hash=sha256:dca6b98db790274a68185d27a65801b58b4caf653a4e556b5f62827509347c7d +sentence-transformers==5.4.1 \ + --hash=sha256:436bcb1182a0ff42a8fb2b1c43498a70d0a75b688d182f2cd0d1dd115af61ddc \ + --hash=sha256:a6d640fc363849b63affb8e140e9d328feabab86f83d58ac3e16b1c28140b790 # via feast (pyproject.toml) setuptools==80.10.2 \ --hash=sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70 \ @@ -5432,9 +5510,9 @@ six==1.17.0 \ # python-dateutil # rfc3339-validator # thriftpy2 -smart-open==7.5.1 \ - --hash=sha256:3e07cbbd9c8a908bcb8e25d48becf1a5cbb4886fa975e9f34c672ed171df2318 \ - --hash=sha256:3f08e16827c4733699e6b2cc40328a3568f900cb12ad9a3ad233ba6c872d9fe7 +smart-open==7.6.0 \ + --hash=sha256:2a78f454610a826aa688065b54b4a0a9b12a5599fa61d5190e9bac2df5e5f53f \ + --hash=sha256:44717f46b5ff276fac03b88e5d13d1c416f064f3b7b081381b0fa8889004bd7e # via ray sniffio==1.3.1 \ --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ @@ -5510,103 +5588,103 @@ sphinxcontrib-serializinghtml==2.0.0 \ --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \ --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d # via sphinx -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot[rs]==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot[rs]==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via # feast (pyproject.toml) # ibis-framework -sqlglotc==30.2.1 \ - --hash=sha256:052cd7bb41fc9b841eb268d4dd601eb6b5954b7c6d5656795d4350a0f8020d53 \ - --hash=sha256:058f0e9aed2b8dff87dc893b8793e514204c8dfef699b7d3d1704dfbdd949f2b \ - --hash=sha256:0e6be524252894c0fa98d25d4e60dfae6485ba66ca1abd40bf05f16a9cf26baf \ - --hash=sha256:13f8f68808777ba7d845bc908bf09f72a0c9899a19811483dc52f0fa48b38d5a \ - --hash=sha256:1a004086ab871be0cc97766f7b6fb8866729f09dd7272254fd31c05107f3fdc8 \ - --hash=sha256:25c6f62f31cd3a051285635c3f6a01d2f3c73ca2baaa26970815166928042ace \ - --hash=sha256:2b5fe8adc1a1e2fb819e014e94974a274f30dbf9684ceed9f171fb0889f80f0b \ - --hash=sha256:2ffe527bc8664b03cc936bae7ebf965f482beb4acee7a815c2ec2d9aea720b4e \ - --hash=sha256:4aa90e08f53409b1857572836e57a31835ed20e32521c6fafdc6af96199baff7 \ - --hash=sha256:507935a971e0a9e5d4ac7ca14df479f8e270502b44904f71d95c0aaed066006f \ - --hash=sha256:515e092ab8fb522b256fa8a34f471e9b187bb8a50a7c0226a65b036a07d6d188 \ - --hash=sha256:585bb610fde3e3dd1d7e5ff3cce14f70fbd53ced6769cd104679adf8b5c4ab5b \ - --hash=sha256:850e7517dd4739cad9af65bcb9699825f9202e5971407bf955e3248fe4814f96 \ - --hash=sha256:8f063af733cbcc51686380470e7f3f80b589b8c58084baa138efb3b8ca821597 \ - --hash=sha256:b17e3002ed10747388367621b2ecf39c06d5fdc6b3c31a8c32be2f5ef546fc0b \ - --hash=sha256:d577e1635e127febb7012bc42fa1c3b958076e59a1a116ade20048c572a1be42 \ - --hash=sha256:dc292cd73e0c447253877c27f00454a2d09b71324a130ad4c58c145ab753889e \ - --hash=sha256:de168df756a21a028cf1f917f92da2f77bb135f3b6cdd960914460942a5eca10 \ - --hash=sha256:de884dd224220002c3e940ca5bdceb27ef9638e5f02493db133ffb8ae88b5610 \ - --hash=sha256:f33c7d1646ff6531cb9b07f0740b2939f3ecaa31efebfbec8adb6b275f1a45f2 \ - --hash=sha256:f9a1fc7b1ff3b51d0d03a391768a79964f68541b4c2f294a25a6f14e6670ffab \ - --hash=sha256:fae4edad0b7c5f9f963bd63452f722f0d7f77a436c2d334b555b31722f9573ad \ - --hash=sha256:fdc19623a1c7659918c3cee18ea8849fc4af9eaeb87247acf37e0393295d32b7 \ - --hash=sha256:feefc0ab7606d1fe284d23bef09ea4829ce4fad679936959c29324310f23e081 \ - --hash=sha256:ff19b7ecb931aef6c7c6168af5530c07e67915102b701d45ae80446f0695ba54 +sqlglotc==30.6.0 \ + --hash=sha256:003b15bbe3f3a4a63313baeb3f090906cf0172fe007ab24974c612a577f56c61 \ + --hash=sha256:05861b5d74ae4b0a5a6e6a9309a8a975d9572c2bf1eb9634a33af9189eb7d333 \ + --hash=sha256:0b907206ef36fd8f0c28da4c5b8c8f896bd67826da0d765616e6c950689ac849 \ + --hash=sha256:0e1aae195008c1d87ed1b1543bceed1408ae8ecf146571b3029e98b23a19c59c \ + --hash=sha256:16291ee3da0276df2689cc139df41d8723cf6014aca979f307fb75a898b0d3e8 \ + --hash=sha256:27d6c22375395f1fdfe8a5d80b5fb781ca7849e29c7db7dfb11edb466a5b7a4d \ + --hash=sha256:2b50aba396d4622c201a9dd933b51cd6858b5af5ebbcf7db1af35db7e83ece48 \ + --hash=sha256:4a5beba24625bc14070992fdddc7aed22df912007882f31f27b0e88d7b7d9445 \ + --hash=sha256:4c84a933816374b6167d9347e488fb4a357bb0e3be2e8e820dafeb3c9948feab \ + --hash=sha256:4d45ee83e1f72ee94045ccfd13e51fa7d822548f50e9d20d3d42e127bcd9f453 \ + --hash=sha256:4eb6f349c21d5a3e39733db5416e57ec171ca3b1c17b02badfdbe55d9a3666a5 \ + --hash=sha256:61a1e4f533955db0bfd4219883bfab1f2030753ffaebc9cbb4e950dfbcae3db7 \ + --hash=sha256:63b02231cc2f10d63df373fa02f3e03b4216e94c6f32ccdfd11adc770dc95fb7 \ + --hash=sha256:6e8dab3b9f84c9b591ecd1fb0920b800c08dd6c38eda3be91176fc239d3c94bc \ + --hash=sha256:7acdb7b15f060bacea01e1c68d1dd70471f03bda89478ca0ee96bac3df03d0a7 \ + --hash=sha256:87437d4f32c1dc61c3ef046dc39b9fe119ac2e2e5253e33351f1a62ba802942a \ + --hash=sha256:875c6925f3d70aa1bc4bcc405090a12deec01de71193e7229f114c5ecf39f725 \ + --hash=sha256:949f5d457c8c98998ed06d060873370d7553077ab3c2db9569acda5887cd6ce7 \ + --hash=sha256:c69c9dbe4dfab74329294d07b2956ca9a03123089a1923f3d299a21d4b66898d \ + --hash=sha256:cb4b5a532fb35ce415aea6d360fe202278b46cc59c07318b97401930a1584e35 \ + --hash=sha256:cfba6244f52bcdffe22a35334d48d741aed3077c20bb7aa9bb511c21fd766438 \ + --hash=sha256:dd781790c3fc6cb82e6c00836f7f9ab33941cf2b9dca173d7b9f41c37c78d114 \ + --hash=sha256:e397befde3d08d870a8f1c7bd80d7abc68ee119c472143ab196709319e205af4 \ + --hash=sha256:f0a37ad865b106005cb5d36efd537a412912477c83cbca1579ee45f17d73fe54 \ + --hash=sha256:fb977d427a196a620aa60b71d4b66d60c3d015c9004e7393162d4771fb435406 # via sqlglot sqlglotrs==0.13.0 \ --hash=sha256:6b934a244b16f26fca50974328a2ebc7689583c59f06203cebb46e2e6e8d93a7 \ @@ -5734,9 +5812,9 @@ thriftpy2==0.6.0 \ --hash=sha256:f6b86112cca7bd04151ce248d781763ea5f74cc18d148476c6d16cee32db81ac \ --hash=sha256:f837ab85ae93b118766b8b28a1cec47a1daddee303e1f986a595c56379062a5c # via happybase -tifffile==2026.3.3 \ - --hash=sha256:d9a1266bed6f2ee1dd0abde2018a38b4f8b2935cb843df381d70ac4eac5458b7 \ - --hash=sha256:e8be15c94273113d31ecb7aa3a39822189dd11c4967e3cc88c178f1ad2fd1170 +tifffile==2026.4.11 \ + --hash=sha256:17758ff0c0d4db385792a083ad3ca51fcb0f4d942642f4d8f8bc1287fdcf17bc \ + --hash=sha256:9b94ffeddb39e97601af646345e8808f885773de01b299e480ed6d3a41509ec9 # via scikit-image timm==1.0.26 \ --hash=sha256:985c330de5ccc3a2aa0224eb7272e6a336084702390bb7e3801f3c91603d3683 \ @@ -6004,15 +6082,16 @@ tree-sitter==0.25.2 \ --hash=sha256:fbb1706407c0e451c4f8cc016fec27d72d4b211fdd3173320b1ada7a6c74c3ac \ --hash=sha256:fe43c158555da46723b28b52e058ad444195afd1db3ca7720c59a254544e9c20 # via docling-core -tree-sitter-c==0.24.1 \ - --hash=sha256:290bff0f9c79c966496ebae45042f77543e6e4aea725f40587a8611d566231a8 \ - --hash=sha256:789781afcb710df34144f7e2a20cd80e325114b9119e3956c6bd1dd2d365df98 \ - --hash=sha256:7d2d0cda0b8dda428c81440c1e94367f9f13548eedca3f49768bde66b1422ad6 \ - --hash=sha256:942bcd7cbecd810dcf7ca6f8f834391ebf0771a89479646d891ba4ca2fdfdc88 \ - --hash=sha256:9a74cfd7a11ca5a961fafd4d751892ee65acae667d2818968a6f079397d8d28c \ - --hash=sha256:9c06ac26a1efdcc8b26a8a6970fbc6997c4071857359e5837d4c42892d45fe1e \ - --hash=sha256:a6a807705a3978911dc7ee26a7ad36dcfacb6adfc13c190d496660ec9bd66707 \ - --hash=sha256:d46bbda06f838c2dcb91daf767813671fd366b49ad84ff37db702129267b46e1 +tree-sitter-c==0.24.2 \ + --hash=sha256:1628584df0299b5a340aa63f8e67b6c97c91517f52fa7e7a4c557e40adb330a9 \ + --hash=sha256:4a2f4371cd816cc3153458f69062135ebb2ea5f275ddd90494e5c823d778204a \ + --hash=sha256:4d4579a8b54f0a442f903d88d3304cab77cd5c2031d4015baa4f2f8e15d6dcb7 \ + --hash=sha256:5041ef67eb68ce6bc8bb0b1f8ef3a5585ce523dae0c7eec109ab0627dd75aede \ + --hash=sha256:82842c5a5f2acd93f4de10038c33ac179c8979defc39376f990348d6289e933b \ + --hash=sha256:97bc80a224d48215d4e6e6376bf30d114f4c317b8145ff1b02afe785d4ba7bdd \ + --hash=sha256:abb549225091f7b25df2dd3a0143ece6e208f7055d8bcb4700b41ee79b9ef1e1 \ + --hash=sha256:c098bedcd5ac86ff93fa734d51d1dd86aed40fd5ed7d634c7af11380a0469969 \ + --hash=sha256:e2b42e8e22202c251f8629306f9321233542e07a6e01611b5fe83489272143eb # via docling-core tree-sitter-javascript==0.25.0 \ --hash=sha256:199d09985190852e0912da2b8d26c932159be314bc04952cf917ed0e4c633e6b \ @@ -6061,9 +6140,9 @@ typer==0.12.5 \ # docling # docling-core # fastapi-mcp -types-cffi==2.0.0.20260402 \ - --hash=sha256:47e1320c009f630c59c55c8e3d2b8c501e280babf52e92f6109cbfb0864ba367 \ - --hash=sha256:f647a400fba0a31d603479169d82ee5359db79bd1136e41dc7e6489296e3a2b2 +types-cffi==2.0.0.20260408 \ + --hash=sha256:68bd296742b4ff7c0afe3547f50bd0acc55416ecf322ffefd2b7344ef6388a42 \ + --hash=sha256:aa8b9c456ab715c079fc655929811f21f331bfb940f4a821987c581bf4e36230 # via types-pyopenssl types-protobuf==3.19.22 \ --hash=sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab \ @@ -6071,25 +6150,25 @@ types-protobuf==3.19.22 \ # via # feast (pyproject.toml) # mypy-protobuf -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) types-pyopenssl==24.1.0.20240722 \ --hash=sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39 \ --hash=sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54 # via types-redis -types-python-dateutil==2.9.0.20260402 \ - --hash=sha256:7827e6a9c93587cc18e766944254d1351a2396262e4abe1510cbbd7601c5e01f \ - --hash=sha256:a980142b9966713acb382c467e35c5cc4208a2f91b10b8d785a0ae6765df6c0b +types-python-dateutil==2.9.0.20260408 \ + --hash=sha256:473139d514a71c9d1fbd8bb328974bedcb1cc3dba57aad04ffa4157f483c216f \ + --hash=sha256:8b056ec01568674235f64ecbcef928972a5fac412f5aab09c516dfa2acfbb582 # via feast (pyproject.toml) -types-pytz==2026.1.1.20260402 \ - --hash=sha256:0d9a60ed1c6ad4fce7c6395b5bd2d9827db41d4b83de7c0322cf85869c2bfda3 \ - --hash=sha256:79209aa51dc003a4a6a764234d92b14e5c09a1b7f24e0f00c493929fd33618e8 +types-pytz==2026.1.1.20260408 \ + --hash=sha256:89b6a34b9198ea2a4b98a9d15cbca987053f52a105fd44f7ce3789cae4349408 \ + --hash=sha256:c7e4dec76221fb7d0c97b91ad8561d689bebe39b6bcb7b728387e7ffd8cde788 # via feast (pyproject.toml) -types-pyyaml==6.0.12.20250915 \ - --hash=sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3 \ - --hash=sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6 +types-pyyaml==6.0.12.20260408 \ + --hash=sha256:92a73f2b8d7f39ef392a38131f76b970f8c66e4c42b3125ae872b7c93b556307 \ + --hash=sha256:fbc42037d12159d9c801ebfcc79ebd28335a7c13b08a4cfbc6916df78fee9384 # via feast (pyproject.toml) types-redis==4.6.0.20241004 \ --hash=sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e \ @@ -6099,15 +6178,15 @@ types-requests==2.30.0.0 \ --hash=sha256:c6cf08e120ca9f0dc4fa4e32c3f953c3fba222bcc1db6b97695bce8da1ba9864 \ --hash=sha256:dec781054324a70ba64430ae9e62e7e9c8e4618c185a5cb3f87a6738251b5a31 # via feast (pyproject.toml) -types-setuptools==82.0.0.20260402 \ - --hash=sha256:4b9a9f6c3c4c65107a3956ad6a6acbccec38e398ff6d5f78d5df7f103dadb8d6 \ - --hash=sha256:63d2b10ba7958396ad79bbc24d2f6311484e452daad4637ffd40407983a27069 +types-setuptools==82.0.0.20260408 \ + --hash=sha256:036c68caf7e672a699f5ebbf914708d40644c14e05298bc49f7272be91cf43d3 \ + --hash=sha256:ece0a215cdfa6463a65fd6f68bd940f39e455729300ddfe61cab1147ed1d2462 # via # feast (pyproject.toml) # types-cffi -types-tabulate==0.10.0.20260308 \ - --hash=sha256:724dcb1330ffba5f46d3cf6e29f45089fccb8e85801e6e7ac9efb1195bf7bea1 \ - --hash=sha256:94a9795965bc6290f844d61e8680a1270040664b88fd12014624090fd847e13c +types-tabulate==0.10.0.20260408 \ + --hash=sha256:2b19d193603d38c34645de53c0c1087e2364487d518d4a2f44268db2366723cc \ + --hash=sha256:903d62fdf7e5a0ff659fd5d629df716232f7658c6d30e98f0374488d06ffacf4 # via feast (pyproject.toml) types-urllib3==1.26.25.14 \ --hash=sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f \ @@ -6162,9 +6241,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # arrow # ibis-framework @@ -6544,9 +6623,9 @@ werkzeug==3.1.8 \ --hash=sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50 \ --hash=sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44 # via moto -wheel==0.46.3 \ - --hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d \ - --hash=sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803 +wheel==0.47.0 \ + --hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \ + --hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3 # via # pip-tools # singlestoredb @@ -6638,6 +6717,7 @@ wrapt==1.17.3 \ --hash=sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c # via # aiobotocore + # deprecated # smart-open # testcontainers xlsxwriter==3.2.9 \ @@ -6920,9 +7000,9 @@ yarl==1.23.0 \ --hash=sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6 \ --hash=sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d # via aiohttp -zipp==3.23.0 \ - --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ - --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 +zipp==3.23.1 \ + --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ + --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 # via importlib-metadata zstandard==0.25.0 \ --hash=sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64 \ diff --git a/sdk/python/requirements/py3.12-minimal-requirements.txt b/sdk/python/requirements/py3.12-minimal-requirements.txt index 71c61613cb2..70b0feb6ac2 100644 --- a/sdk/python/requirements/py3.12-minimal-requirements.txt +++ b/sdk/python/requirements/py3.12-minimal-requirements.txt @@ -190,9 +190,9 @@ botocore==1.38.46 \ # boto3 # s3transfer # snowflake-connector-python -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # httpcore # httpx @@ -420,9 +420,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -436,56 +436,56 @@ colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via feast (pyproject.toml) -cryptography==46.0.6 \ - --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ - --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ - --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ - --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ - --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ - --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ - --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ - --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ - --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ - --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ - --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ - --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ - --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ - --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ - --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ - --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ - --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ - --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ - --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ - --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ - --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ - --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ - --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ - --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ - --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ - --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ - --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ - --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ - --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ - --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ - --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ - --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ - --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ - --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ - --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ - --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ - --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ - --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ - --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ - --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 +cryptography==47.0.0 \ + --hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \ + --hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \ + --hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \ + --hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \ + --hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \ + --hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \ + --hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \ + --hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \ + --hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \ + --hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \ + --hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \ + --hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \ + --hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \ + --hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \ + --hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \ + --hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \ + --hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \ + --hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \ + --hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \ + --hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \ + --hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \ + --hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \ + --hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \ + --hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \ + --hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \ + --hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \ + --hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \ + --hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \ + --hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \ + --hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \ + --hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \ + --hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \ + --hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \ + --hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \ + --hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \ + --hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \ + --hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \ + --hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \ + --hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \ + --hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \ + --hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \ + --hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \ + --hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \ + --hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \ + --hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \ + --hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \ + --hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \ + --hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \ + --hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736 # via # google-auth # pyjwt @@ -505,50 +505,50 @@ dill==0.3.9 \ --hash=sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a \ --hash=sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c # via feast (pyproject.toml) -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ --hash=sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286 # via kubernetes -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -556,9 +556,9 @@ fastapi-mcp==0.4.0 \ --hash=sha256:d4a3fe7966af24d44e4b412720561c95eb12bed999a4443a88221834b3b15aec \ --hash=sha256:d4ca9410996f4c7b8ea0d7b20fdf79878dc359ebf89cbf3b222e0b675a55097d # via feast (pyproject.toml) -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via snowflake-connector-python frozenlist==1.8.0 \ --hash=sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686 \ @@ -700,9 +700,9 @@ fsspec==2024.9.0 \ # via # feast (pyproject.toml) # dask -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -712,9 +712,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-datastore # google-cloud-storage # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -1068,9 +1068,9 @@ ibis-framework[duckdb]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1102,97 +1102,97 @@ kubernetes==35.0.0 \ --hash=sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d \ --hash=sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee # via feast (pyproject.toml) -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -1569,51 +1569,51 @@ multidict==6.7.1 \ # aiobotocore # aiohttp # yarl -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -1703,9 +1703,9 @@ oauthlib==3.3.1 \ --hash=sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9 \ --hash=sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1 # via requests-oauthlib -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # db-dtypes @@ -1779,9 +1779,9 @@ pandas==2.3.3 \ # pandas-gbq # pymilvus # snowflake-connector-python -pandas-gbq==0.34.1 \ - --hash=sha256:6bea5b85937251b976cf9db38151ea59abbff98771179183488d4614694bff67 \ - --hash=sha256:b74932c6ee35dfc81582f39c792e3a68c9ef9bee8c85f25667d9d05dfadd0daf +pandas-gbq==0.35.0 \ + --hash=sha256:258de481019566611031919997bf9c1ece4ca30a4dd02d3fc3664b251d446182 \ + --hash=sha256:596c908487ef0649a161e86ef272c00c267e581b95dc5ee0dc3518545b33bcfc # via google-cloud-bigquery parsy==2.2 \ --hash=sha256:5e981613d9d2d8b68012d1dd0afe928967bea2e4eefdb76c2f545af0dd02a9e7 \ @@ -1791,17 +1791,17 @@ partd==1.4.2 \ --hash=sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f \ --hash=sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c # via dask -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via mypy -platformdirs==4.9.4 \ - --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ - --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 +platformdirs==4.9.6 \ + --hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \ + --hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917 # via snowflake-connector-python -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) propcache==0.4.1 \ --hash=sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e \ @@ -1997,57 +1997,57 @@ psycopg-pool==3.3.0 \ --hash=sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 \ --hash=sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5 # via psycopg -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -2072,141 +2072,140 @@ pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi # fastapi-mcp # mcp # pydantic-settings -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # fastapi-mcp # mcp @@ -2235,9 +2234,9 @@ pymysql==1.1.2 \ --hash=sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03 \ --hash=sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9 # via feast (pyproject.toml) -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python python-dateutil==2.9.0.post0 \ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ @@ -2256,9 +2255,9 @@ python-dotenv==1.2.2 \ # pydantic-settings # pymilvus # uvicorn -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp pytz==2026.1.post1 \ --hash=sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1 \ @@ -2373,9 +2372,9 @@ requests-oauthlib==2.0.0 \ # via # google-auth-oauthlib # kubernetes -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 # via # fastapi-mcp # ibis-framework @@ -2552,74 +2551,74 @@ sortedcontainers==2.4.0 \ --hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \ --hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0 # via snowflake-connector-python -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via ibis-framework sse-starlette==3.3.4 \ --hash=sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1 \ @@ -2714,13 +2713,13 @@ typeguard==4.5.1 \ --hash=sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40 \ --hash=sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274 # via feast (pyproject.toml) -typer==0.24.1 \ - --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e \ - --hash=sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45 +typer==0.24.2 \ + --hash=sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8 \ + --hash=sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480 # via fastapi-mcp -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -2751,9 +2750,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # ibis-framework # pandas diff --git a/sdk/python/requirements/py3.12-minimal-sdist-requirements-build.txt b/sdk/python/requirements/py3.12-minimal-sdist-requirements-build.txt index 15c53b6ba7e..46228c310e0 100644 --- a/sdk/python/requirements/py3.12-minimal-sdist-requirements-build.txt +++ b/sdk/python/requirements/py3.12-minimal-sdist-requirements-build.txt @@ -208,9 +208,9 @@ cython==3.2.4 \ # via # pyarrow # uvloop -dunamai==1.26.0 \ - --hash=sha256:5396ac43aa20ed059040034e9f9798c7464cf4334c6fc3da3732e29273a2f97d \ - --hash=sha256:f584edf0fda0d308cce0961f807bc90a8fe3d9ff4d62f94e72eca7b43f0ed5f6 +dunamai==1.26.1 \ + --hash=sha256:2727d939c5b4257cb01ea404372803b477f5176e5a347c43beaf89cd5072e853 \ + --hash=sha256:3b46007bd65b00b4824ead0a1aee365fd22d0ec2b9c219497d4fd48f52860c8b # via uv-dynamic-versioning expandvars==1.1.2 \ --hash=sha256:6c5822b7b756a99a356b915dd1267f52ab8a4efaa135963bd7f4bd5d368f71d7 \ @@ -250,9 +250,9 @@ gitdb==4.0.12 \ --hash=sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571 \ --hash=sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf # via gitpython -gitpython==3.1.46 \ - --hash=sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f \ - --hash=sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058 +gitpython==3.1.47 \ + --hash=sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905 \ + --hash=sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd # via pymilvus hatch-fancy-pypi-readme==25.1.0 \ --hash=sha256:9c58ed3dff90d51f43414ce37009ad1d5b0f08ffc9fc216998a06380f01c0045 \ @@ -319,97 +319,164 @@ jinja2==3.1.6 \ --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 # via uv-dynamic-versioning -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +libcst==1.8.6 \ + --hash=sha256:04030ea4d39d69a65873b1d4d877def1c3951a7ada1824242539e399b8763d30 \ + --hash=sha256:06fc56335a45d61b7c1b856bfab4587b84cfe31e9d6368f60bb3c9129d900f58 \ + --hash=sha256:089c58e75cb142ec33738a1a4ea7760a28b40c078ab2fd26b270dac7d2633a4d \ + --hash=sha256:08bd63a8ce674be431260649e70fca1d43f1554f1591eac657f403ff8ef82c7a \ + --hash=sha256:0c13d5bd3d8414a129e9dccaf0e5785108a4441e9b266e1e5e9d1f82d1b943c9 \ + --hash=sha256:0cbe17067055829607c5ba4afa46bfa4d0dd554c0b5a583546e690b7367a29b6 \ + --hash=sha256:16cfe0cfca5fd840e1fb2c30afb628b023d3085b30c3484a79b61eae9d6fe7ba \ + --hash=sha256:1a3a5e4ee870907aa85a4076c914ae69066715a2741b821d9bf16f9579de1105 \ + --hash=sha256:1dc3b897c8b0f7323412da3f4ad12b16b909150efc42238e19cbf19b561cc330 \ + --hash=sha256:203ec2a83f259baf686b9526268cd23d048d38be5589594ef143aee50a4faf7e \ + --hash=sha256:207481197afd328aa91d02670c15b48d0256e676ce1ad4bafb6dc2b593cc58f1 \ + --hash=sha256:25eaeae6567091443b5374b4c7d33a33636a2d58f5eda02135e96fc6c8807786 \ + --hash=sha256:25fc7a1303cad7639ad45ec38c06789b4540b7258e9a108924aaa2c132af4aca \ + --hash=sha256:2f04d3672bde1704f383a19e8f8331521abdbc1ed13abb349325a02ac56e5012 \ + --hash=sha256:351ab879c2fd20d9cb2844ed1ea3e617ed72854d3d1e2b0880ede9c3eea43ba8 \ + --hash=sha256:36473e47cb199b7e6531d653ee6ffed057de1d179301e6c67f651f3af0b499d6 \ + --hash=sha256:3649a813660fbffd7bc24d3f810b1f75ac98bd40d9d6f56d1f0ee38579021073 \ + --hash=sha256:375965f34cc6f09f5f809244d3ff9bd4f6cb6699f571121cebce53622e7e0b86 \ + --hash=sha256:3a926a4b42015ee24ddfc8ae940c97bd99483d286b315b3ce82f3bafd9f53474 \ + --hash=sha256:3f4fbb7f569e69fd9e89d9d9caa57ca42c577c28ed05062f96a8c207594e75b8 \ + --hash=sha256:42a4f68121e2e9c29f49c97f6154e8527cd31021809cc4a941c7270aa64f41aa \ + --hash=sha256:44f38139fa95e488db0f8976f9c7ca39a64d6bc09f2eceef260aa1f6da6a2e42 \ + --hash=sha256:455f49a93aea4070132c30ebb6c07c2dea0ba6c1fde5ffde59fc45dbb9cfbe4b \ + --hash=sha256:4d7bbdd35f3abdfb5ac5d1a674923572dab892b126a58da81ff2726102d6ec2e \ + --hash=sha256:4fc3fef8a2c983e7abf5d633e1884c5dd6fa0dcb8f6e32035abd3d3803a3a196 \ + --hash=sha256:536567441182a62fb706e7aa954aca034827b19746832205953b2c725d254a93 \ + --hash=sha256:5432e785322aba3170352f6e72b32bea58d28abd141ac37cc9b0bf6b7c778f58 \ + --hash=sha256:55ec021a296960c92e5a33b8d93e8ad4182b0eab657021f45262510a58223de1 \ + --hash=sha256:59a7e388c57d21d63722018978a8ddba7b176e3a99bd34b9b84a576ed53f2978 \ + --hash=sha256:5dcaaebc835dfe5755bc85f9b186fb7e2895dda78e805e577fef1011d51d5a5c \ + --hash=sha256:6366ab2107425bf934b0c83311177f2a371bfc757ee8c6ad4a602d7cbcc2f363 \ + --hash=sha256:6421a930b028c5ef4a943b32a5a78b7f1bf15138214525a2088f11acbb7d3d64 \ + --hash=sha256:6609291c41f7ad0bac570bfca5af8fea1f4a27987d30a1fa8b67fe5e67e6c78d \ + --hash=sha256:6a65f844d813ab4ef351443badffa0ae358f98821561d19e18b3190f59e71996 \ + --hash=sha256:6aa11df6c58812f731172b593fcb485d7ba09ccc3b52fea6c7f26a43377dc748 \ + --hash=sha256:6b23d14a7fc0addd9795795763af26b185deb7c456b1e7cc4d5228e69dab5ce8 \ + --hash=sha256:6cad63e3a26556b020b634d25a8703b605c0e0b491426b3e6b9e12ed20f09100 \ + --hash=sha256:6d8b67874f2188399a71a71731e1ba2d1a2c3173b7565d1cc7ffb32e8fbaba5b \ + --hash=sha256:72cca15800ffc00ba25788e4626189fe0bc5fe2a0c1cb4294bce2e4df21cc073 \ + --hash=sha256:7445479ebe7d1aff0ee094ab5a1c7718e1ad78d33e3241e1a1ec65dcdbc22ffb \ + --hash=sha256:7f04febcd70e1e67917be7de513c8d4749d2e09206798558d7fe632134426ea4 \ + --hash=sha256:8066f1b70f21a2961e96bedf48649f27dfd5ea68be5cd1bed3742b047f14acde \ + --hash=sha256:819c8081e2948635cab60c603e1bbdceccdfe19104a242530ad38a36222cb88f \ + --hash=sha256:85b7025795b796dea5284d290ff69de5089fc8e989b25d6f6f15b6800be7167f \ + --hash=sha256:87e74f7d7dfcba9efa91127081e22331d7c42515f0a0ac6e81d4cf2c3ed14661 \ + --hash=sha256:8a434c521fadaf9680788b50d5c21f4048fa85ed19d7d70bd40549fbaeeecab1 \ + --hash=sha256:98fa1ca321c81fb1f02e5c43f956ca543968cc1a30b264fd8e0a2e1b0b0bf106 \ + --hash=sha256:a20c5182af04332cc94d8520792befda06d73daf2865e6dddc5161c72ea92cb9 \ + --hash=sha256:b0d8c364c44ae343937f474b2e492c1040df96d94530377c2f9263fb77096e4f \ + --hash=sha256:b188e626ce61de5ad1f95161b8557beb39253de4ec74fc9b1f25593324a0279c \ + --hash=sha256:b6c1248cc62952a3a005792b10cdef2a4e130847be9c74f33a7d617486f7e532 \ + --hash=sha256:ba9ab2b012fbd53b36cafd8f4440a6b60e7e487cd8b87428e57336b7f38409a4 \ + --hash=sha256:bb9b4077bdf8857b2483879cbbf70f1073bc255b057ec5aac8a70d901bb838e9 \ + --hash=sha256:bdb14bc4d4d83a57062fed2c5da93ecb426ff65b0dc02ddf3481040f5f074a82 \ + --hash=sha256:bff00e1c766658adbd09a175267f8b2f7616e5ee70ce45db3d7c4ce6d9f6bec7 \ + --hash=sha256:c0a0cc80aebd8aa15609dd4d330611cbc05e9b4216bcaeabba7189f99ef07c28 \ + --hash=sha256:c188d06b583900e662cd791a3f962a8c96d3dfc9b36ea315be39e0a4c4792ebf \ + --hash=sha256:c41c76e034a1094afed7057023b1d8967f968782433f7299cd170eaa01ec033e \ + --hash=sha256:c9d7aeafb1b07d25a964b148c0dda9451efb47bbbf67756e16eeae65004b0eb5 \ + --hash=sha256:cb2679ef532f9fa5be5c5a283b6357cb6e9888a8dd889c4bb2b01845a29d8c0b \ + --hash=sha256:da95b38693b989eaa8d32e452e8261cfa77fe5babfef1d8d2ac25af8c4aa7e6d \ + --hash=sha256:e00e275d4ba95d4963431ea3e409aa407566a74ee2bf309a402f84fc744abe47 \ + --hash=sha256:f1472eeafd67cdb22544e59cf3bfc25d23dc94058a68cf41f6654ff4fcb92e09 \ + --hash=sha256:f729c37c9317126da9475bdd06a7208eb52fcbd180a6341648b45a56b4ba708b \ + --hash=sha256:fea5c7fa26556eedf277d4f72779c5ede45ac3018650721edd77fd37ccd4a2d4 + # via pyarrow +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy markupsafe==3.0.3 \ --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ @@ -502,21 +569,21 @@ markupsafe==3.0.3 \ --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 # via jinja2 -maturin==1.12.6 \ - --hash=sha256:06fc8d089f98623ce924c669b70911dfed30f9a29956c362945f727f9abc546b \ - --hash=sha256:2cb41139295eed6411d3cdafc7430738094c2721f34b7eeb44f33cac516115dc \ - --hash=sha256:351f3af1488a7cbdcff3b6d8482c17164273ac981378a13a4a9937a49aec7d71 \ - --hash=sha256:3f32e0a3720b81423c9d35c14e728cb1f954678124749776dc72d533ea1115e8 \ - --hash=sha256:6892b4176992fcc143f9d1c1c874a816e9a041248eef46433db87b0f0aff4278 \ - --hash=sha256:6dbddfe4dc7ddee60bbac854870bd7cfec660acb54d015d24597d59a1c828f61 \ - --hash=sha256:75133e56274d43b9227fd49dca9a86e32f1fd56a7b55544910c4ce978c2bb5aa \ - --hash=sha256:8fdb0f63e77ee3df0f027a120e9af78dbc31edf0eb0f263d55783c250c33b728 \ - --hash=sha256:977290159d252db946054a0555263c59b3d0c7957135c69e690f4b1558ee9983 \ - --hash=sha256:bae91976cdc8148038e13c881e1e844e5c63e58e026e8b9945aa2d19b3b4ae89 \ - --hash=sha256:c0c742beeeef7fb93b6a81bd53e75507887e396fd1003c45117658d063812dad \ - --hash=sha256:d37be3a811a7f2ee28a0fa0964187efa50e90f21da0c6135c27787fa0b6a89db \ - --hash=sha256:e90dc12bc6a38e9495692a36c9e231c4d7e0c9bfde60719468ab7d8673db3c45 \ - --hash=sha256:fa84b7493a2e80759cacc2e668fa5b444d55b9994e90707c42904f55d6322c1e +maturin==1.13.1 \ + --hash=sha256:001741c6cff56aa8ea59a0d78ae990c0550d0e3e82b00b683eedb4158a8ef7e6 \ + --hash=sha256:01c845825c917c07c1d0b2c9032c59c16a7d383d1e649a46481d3e5693c2750f \ + --hash=sha256:2839024dcd65776abb4759e5bca29941971e095574162a4d335191da4be9ff24 \ + --hash=sha256:3da18cccf2f683c0977bff9146a0908d6ffce836d600665736ac01679f588cb9 \ + --hash=sha256:416e4e01cb88b798e606ee43929df897e42c1647b722ef68283816cca99a8742 \ + --hash=sha256:6b1e5916a253243e8f5f9e847b62bbc98420eec48c9ce2e2e8724c6da89d359b \ + --hash=sha256:72888e87819ce546d0d2df900e4b385e4ef299077d92ee37b48923a5602dae94 \ + --hash=sha256:98b5fcf1a186c217830a8295ecc2989c6b1cf50945417adfc15252107b9475b7 \ + --hash=sha256:9a87ff3b8e4d1c6eac33ebfe8e261e8236516d98d45c0323550621819b5a1a2f \ + --hash=sha256:a2017d2281203d0c6570240e7d746564d766d756105823b7de68bda6ae722711 \ + --hash=sha256:c1490584f3c70af45466ee99065b49e6657ebdccac6b10571bb44681309c9396 \ + --hash=sha256:c6a720b252c99de072922dbe4432ab19662b6f80045b0355fec23bdfccb450da \ + --hash=sha256:dc91031e0619c1e28730279ef9ee5f106c9b9ec806b013f888676b242f892eb7 \ + --hash=sha256:f69093ed4a0e6464e52a7fc26d714f859ce15630ec8070743398c6bf41f38a9e # via # cryptography # pydantic-core @@ -616,9 +683,9 @@ numpy==2.4.4 \ # via # pandas # pyarrow -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # hatchling # meson-python @@ -628,9 +695,9 @@ packaging==26.0 \ # setuptools-scm # vcs-versioning # wheel -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via # hatchling # mypy @@ -659,13 +726,13 @@ poetry-core==2.3.2 \ # pkgconfig # rich # tomlkit -pybind11-global==3.0.3 \ - --hash=sha256:141adb150fdb84f6eba3e27241da886f4582574a3d1c30568bf33c1ed3ec8b82 \ - --hash=sha256:7a75ee81e903ea15bdf05db1342c37400751a72316b6620c800b66d70be45632 +pybind11-global==3.0.4 \ + --hash=sha256:95b693c3d646c6b7217a97156a36b6d40305505d4a5a728082da6fe75ada29f5 \ + --hash=sha256:a73e2ebd29f4ee5d7c754b495d555cd703f2cd26c84abe360ee56411dcd7834d # via pybind11 -pybind11==3.0.3 \ - --hash=sha256:00471cdb816882c484708bc5dde80815c8c11cea540ab2cc6410f5ddea434755 \ - --hash=sha256:fb5f8e4a64946b4dcc0451c83a8c384f803bc0a62dd1ba02f199e97dbc9aad4c +pybind11==3.0.4 \ + --hash=sha256:3286b59c8a774b9ee650169302dd5a4eedc30a8617905a0560dd8ee44775130c \ + --hash=sha256:961720ee652da51d531b7b2451a6bd2bc042b0106e6d9baa48ecb7d58034ce63 # via duckdb pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ @@ -675,12 +742,88 @@ pyproject-metadata==0.11.0 \ --hash=sha256:85bbecca8694e2c00f63b492c96921d6c228454057c88e7c352b2077fcaa4096 \ --hash=sha256:c72fa49418bb7c5a10f25e050c418009898d1c051721d19f98a6fb6da59a66cf # via meson-python +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via libcst scikit-build-core==0.12.2 \ --hash=sha256:562e0bbc9de1a354c87825ccf732080268d6582a0200f648e8c4a2dcb1e3736d \ --hash=sha256:6ea4730da400f9a998ec3287bd3ebc1d751fe45ad0a93451bead8618adbc02b1 # via # duckdb # patchelf + # pyarrow # pybind11 # pybind11-global semantic-version==2.10.0 \ @@ -694,7 +837,9 @@ setuptools-git-versioning==3.0.1 \ setuptools-rust==1.12.1 \ --hash=sha256:85ae70989d96c9cfeb5ef79cf3bac2d5200bc5564f720a06edceedbdf6664640 \ --hash=sha256:b7ebd6a182e7aefa97a072e880530c9b0ec8fcca8617e0bb8ff299c1a064f693 - # via maturin + # via + # libcst + # maturin setuptools-scm==10.0.5 \ --hash=sha256:bbba8fe754516cdefd017f4456721775e6ef9662bd7887fb52ae26813d4838c3 \ --hash=sha256:f611037d8aae618221503b8fa89319f073438252ae3420e01c9ceec249131a0a @@ -704,6 +849,7 @@ setuptools-scm==10.0.5 \ # duckdb # hatch-vcs # httpx-sse + # libcst # pluggy # pyarrow # pybindgen @@ -790,9 +936,9 @@ types-psutil==7.0.0.20250218 \ --hash=sha256:1447a30c282aafefcf8941ece854e1100eee7b0296a9d9be9977292f0269b121 \ --hash=sha256:1e642cdafe837b240295b23b1cbd4691d80b08a07d29932143cbbae30eb0db9c # via mypy -types-setuptools==82.0.0.20260402 \ - --hash=sha256:4b9a9f6c3c4c65107a3956ad6a6acbccec38e398ff6d5f78d5df7f103dadb8d6 \ - --hash=sha256:63d2b10ba7958396ad79bbc24d2f6311484e452daad4637ffd40407983a27069 +types-setuptools==82.0.0.20260408 \ + --hash=sha256:036c68caf7e672a699f5ebbf914708d40644c14e05298bc49f7272be91cf43d3 \ + --hash=sha256:ece0a215cdfa6463a65fd6f68bd940f39e455729300ddfe61cab1147ed1d2462 # via mypy typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -814,12 +960,13 @@ versioneer==0.29 \ # via # pandas # partd -wheel==0.46.3 \ - --hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d \ - --hash=sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803 +wheel==0.47.0 \ + --hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \ + --hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3 # via # google-crc32c # httpx-sse + # libcst # meson # mmh3 # pandas @@ -861,6 +1008,7 @@ setuptools==80.10.2 \ # grpc-google-iam-v1 # gunicorn # httpx-sse + # libcst # librt # markupsafe # maturin @@ -879,7 +1027,6 @@ setuptools==80.10.2 \ # psycopg # psycopg-c # psycopg-pool - # pyarrow # pyasn1 # pyasn1-modules # pycparser @@ -903,7 +1050,6 @@ setuptools==80.10.2 \ # tqdm # trove-classifiers # typeguard - # types-pymysql # tzdata # ujson # uvloop @@ -921,4 +1067,5 @@ setuptools==82.0.1 \ --hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb # via # python-dateutil + # types-pymysql # types-setuptools diff --git a/sdk/python/requirements/py3.12-minimal-sdist-requirements.txt b/sdk/python/requirements/py3.12-minimal-sdist-requirements.txt index e24f4ca749b..7ddcab55235 100644 --- a/sdk/python/requirements/py3.12-minimal-sdist-requirements.txt +++ b/sdk/python/requirements/py3.12-minimal-sdist-requirements.txt @@ -202,9 +202,9 @@ calver==2025.3.31 \ --hash=sha256:07511edf5e7fa75ae97445c8c5921240e0fe62937289a3ebe6963eddd3c691b6 \ --hash=sha256:255d1a70bba8f97dc1eee3af4240ed35980508da69257feef94c79e5c6545fc7 # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # httpcore # httpx @@ -432,9 +432,9 @@ charset-normalizer==3.4.7 \ # via # requests # snowflake-connector-python -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -448,56 +448,56 @@ colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via feast (pyproject.toml) -cryptography==46.0.6 \ - --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \ - --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \ - --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \ - --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \ - --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \ - --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \ - --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \ - --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \ - --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \ - --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \ - --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \ - --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \ - --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \ - --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \ - --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \ - --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \ - --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \ - --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \ - --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \ - --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \ - --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \ - --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \ - --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \ - --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \ - --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \ - --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \ - --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \ - --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \ - --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \ - --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \ - --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \ - --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \ - --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \ - --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \ - --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \ - --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \ - --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \ - --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \ - --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \ - --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \ - --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \ - --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \ - --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \ - --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \ - --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \ - --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \ - --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \ - --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \ - --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4 +cryptography==47.0.0 \ + --hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \ + --hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \ + --hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \ + --hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \ + --hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \ + --hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \ + --hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \ + --hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \ + --hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \ + --hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \ + --hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \ + --hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \ + --hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \ + --hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \ + --hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \ + --hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \ + --hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \ + --hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \ + --hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \ + --hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \ + --hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \ + --hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \ + --hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \ + --hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \ + --hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \ + --hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \ + --hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \ + --hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \ + --hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \ + --hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \ + --hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \ + --hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \ + --hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \ + --hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \ + --hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \ + --hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \ + --hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \ + --hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \ + --hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \ + --hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \ + --hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \ + --hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \ + --hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \ + --hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \ + --hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \ + --hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \ + --hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \ + --hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \ + --hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736 # via # google-auth # pyjwt @@ -587,50 +587,50 @@ docutils==0.22.4 \ --hash=sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968 \ --hash=sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de # via sphinx -duckdb==1.5.1 \ - --hash=sha256:054ad424b051b334052afac58cb216f3b1ebb8579fc8c641e60f0182e8725ea9 \ - --hash=sha256:05fc91767d0cfc4cf2fa68966ab5b479ac07561752e42dd0ae30327bd160f64a \ - --hash=sha256:0a6acc2040bec1f05de62a2f3f68f4c12f3ec7d6012b4317d0ab1a195af26225 \ - --hash=sha256:26e56b5f0c96189e3288d83cf7b476e23615987902f801e5788dee15ee9f24a9 \ - --hash=sha256:36e8e32621a9e2a9abe75dc15a4b54a3997f2d8b1e53ad754bae48a083c91130 \ - --hash=sha256:40c5220ec93790b18ec6278da9c6ac2608d997ee6d6f7cd44c5c3992764e8e71 \ - --hash=sha256:446d500a2977c6ae2077f340c510a25956da5c77597175c316edfa87248ceda3 \ - --hash=sha256:46f92ada9023e59f27edc048167b31ac9a03911978b1296c845a34462a27f096 \ - --hash=sha256:482f8a13f2600f527e427f73c42b5aa75536f9892868068f0aaf573055a0135f \ - --hash=sha256:553c273a6a8f140adaa6da6a6135c7f95bdc8c2e5f95252fcdf9832d758e2141 \ - --hash=sha256:5ae7c0d744d64e2753149634787cc4ab60f05ef1e542b060eeab719f3cdb7723 \ - --hash=sha256:5d4147422d91ccdc2d2abf6ed24196025e020259d1d267970ae20c13c2ce84b1 \ - --hash=sha256:6af347debc8b721aa72e48671166282da979d5e5ae52dbc660ab417282b48e23 \ - --hash=sha256:6ba302115f63f6482c000ccfd62efdb6c41d9d182a5bcd4a90e7ab8cd13856eb \ - --hash=sha256:6f7361d66cc801d9eb4df734b139cd7b0e3c257a16f3573ebd550ddb255549e6 \ - --hash=sha256:715f05ea198d20d7f8b407b9b84e0023d17f2b9096c194cea702b7840e74f1f7 \ - --hash=sha256:71dddcebbc5a70e946a06c30b59b5dd7999c9833d307168f90fb4e4b672ab63e \ - --hash=sha256:8150c569b2aa4573b51ba8475e814aa41fd53a3d510c1ffb96f1139f46faf611 \ - --hash=sha256:8843bd9594e1387f1e601439e19ad73abdf57356104fd1e53a708255bb95a13d \ - --hash=sha256:8c0088765747ae5d6c9f89987bb36f9fb83564f07090d721344ce8e1abedffea \ - --hash=sha256:972d0dbf283508f9bc446ee09c3838cb7c7f114b5bdceee41753288c97fe2f7c \ - --hash=sha256:a28531cee2a5a42d89f9ba4da53bfeb15681f12acc0263476c8705380dadce07 \ - --hash=sha256:a3be2072315982e232bfe49c9d3db0a59ba67b2240a537ef42656cc772a887c7 \ - --hash=sha256:ac2804043bd1bc10b5da18f8f4c706877197263a510c41be9b4c0062f5783dcc \ - --hash=sha256:afab8b4b1f4469c3879bb049dd039f8fce402712050324e9524a43d7324c5e87 \ - --hash=sha256:b370d1620a34a4538ef66524fcee9de8171fa263c701036a92bc0b4c1f2f9c6d \ - --hash=sha256:b8b0808dba0c63b7633bdaefb34e08fe0612622224f9feb0e7518904b1615101 \ - --hash=sha256:bc7ca6a1a40e7e4c933017e6c09ef18032add793df4e42624c6c0c87e0bebdad \ - --hash=sha256:caa65e1f5bf007430bf657c37cab7ab81a4ddf8d337e3062bcc5085d17ef038b \ - --hash=sha256:d68c5a01a283cb13b79eafe016fe5869aa11bff8c46e7141c70aa0aac808010f \ - --hash=sha256:da137802688190835b4c863cafa77fd7e29dff662ee6d905a9ffc14f00299c91 \ - --hash=sha256:e56a20ab6cdb90a95b0c99652e28de3504ce77129087319c03c9098266183ae5 \ - --hash=sha256:e878ccb7d20872065e1597935fdb5e65efa43220c8edd0d9c4a1a7ff1f3eb277 \ - --hash=sha256:eba81e0b3011c1f23df7ea47ef4ffaa8239817959ae291515b6efd068bde2161 \ - --hash=sha256:ed6d23a3f806898e69c77430ebd8da0c79c219f97b9acbc9a29a653e09740c59 +duckdb==1.5.2 \ + --hash=sha256:0b291786014df1133f8f18b9df4d004484613146e858d71a21791e0fcca16cf4 \ + --hash=sha256:2323c1195c10fb2bb982fc0218c730b43d1b92a355d61e68e3c5f3ac9d44c34f \ + --hash=sha256:2a1de4f4d454b8c97aec546c82003fc834d3422ce4bc6a19902f3462ef293bed \ + --hash=sha256:35579b8e3a064b5eaf15b0eafc558056a13f79a0a62e34cc4baf57119daecfec \ + --hash=sha256:376856066c65ccd55fcb3a380bbe33a71ce089fc4623d229ffc6e82251afdb6d \ + --hash=sha256:411ad438bd4140f189a10e7f515781335962c5d18bd07837dc6d202e3985253d \ + --hash=sha256:4420b3f47027a7849d0e1815532007f377fa95ee5810b47ea717d35525c12f79 \ + --hash=sha256:486c862bf7f163c0110b6d85b3e5c031d224a671cca468f12ebb1d3a348f6b39 \ + --hash=sha256:52a21823f3fbb52f0f0e5425e20b07391ad882464b955879499b5ff0b45a376b \ + --hash=sha256:5596bbfc31b1b259db69c8d847b42d036ce2c4804f9ccb28f9fc46a16de7bc53 \ + --hash=sha256:56d38b3c4e0ef2abb58898d0fd423933999ed535c45e75e9d9f72e1d5fed69b8 \ + --hash=sha256:638da0d5102b6cb6f7d47f83d0600708ac1d3cb46c5e9aaabc845f9ba4d69246 \ + --hash=sha256:63bf8687feefeed51adf45fa3b062ab8b1b1c350492b7518491b86bae68b1da1 \ + --hash=sha256:6b0fe75c148000f060aa1a27b293cacc0ea08cc1cad724fbf2143d56070a3785 \ + --hash=sha256:70631c847ca918ee710ec874241b00cf9d2e5be90762cbb2a0389f17823c08f7 \ + --hash=sha256:7f69164b048e498b9e9140a24343108a5ae5f17bfb3485185f55fdf9b1aa924d \ + --hash=sha256:81fc4fbf0b5e25840b39ba2a10b78c6953c0314d5d0434191e7898f34ab1bba3 \ + --hash=sha256:84b193aca20565dedb3172de15f843c659c3a6c773bf14843a9bd781c850e7db \ + --hash=sha256:8d599758b4e48bf12e18c9b960cf491d219f0c4972d19a45489c05cc5ab36f83 \ + --hash=sha256:8dbd7e31e5dc157bfe8803fa7d2652336265c6c19926c5a4a9b40f8222868d08 \ + --hash=sha256:98c0535cd6d901f61a5ea3c2e26a1fd28482953d794deb183daf568e3aa5dda6 \ + --hash=sha256:a9cd5e71702d446613750405cde03f66ed268f4c321da071b0472759dad19536 \ + --hash=sha256:bb42e6ed543902e14eae647850da24103a89f0bc2587dec5601b1c1f213bd2ed \ + --hash=sha256:be37680ddb380015cb37318e378c53511c45c4f0d8fac5599d22b7d092b9217a \ + --hash=sha256:c69907354ffee94ba8cf782daf0480dab7557f21ce27fffa6c0ea8f74ed4b8e2 \ + --hash=sha256:c99ef73a277c8921bc0a1f16dee38d924484251d9cfd20951748c20fcd5ed855 \ + --hash=sha256:c9f3e0b71b8a50fccfb42794899285d9d318ce2503782b9dd54868e5ecd0ad31 \ + --hash=sha256:ce0b8141a10d37ecef729c45bc41d334854013f4389f1488bd6035c5579aaac1 \ + --hash=sha256:ce17670bb392ea1b3650537db02bd720908776b5b95f6d2472d31a7de59d1dc1 \ + --hash=sha256:d72b8856b1839d35648f38301b058f6232f4d36b463fe4dc8f4d3fdff2df1a2e \ + --hash=sha256:d9b4f5430bf4f05d4c0dc4c55c75def3a5af4be0343be20fa2bfc577343fbfc9 \ + --hash=sha256:e6495b00cad16888384119842797c49316a96ae1cb132bb03856d980d95afee1 \ + --hash=sha256:ea58ff5b0880593a280cf5511734b17711b32ee1f58b47d726e8600848358160 \ + --hash=sha256:ef461bca07313412dc09961c4a4757a851f56b95ac01c58fac6007632b7b94f2 \ + --hash=sha256:fc85a5dbcbe6eccac1113c72370d1d3aacfdd49198d63950bdf7d8638a307f00 # via ibis-framework durationpy==0.10 \ --hash=sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba \ --hash=sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286 # via kubernetes -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via # feast (pyproject.toml) # fastapi-mcp @@ -638,9 +638,9 @@ fastapi-mcp==0.4.0 \ --hash=sha256:d4a3fe7966af24d44e4b412720561c95eb12bed999a4443a88221834b3b15aec \ --hash=sha256:d4ca9410996f4c7b8ea0d7b20fdf79878dc359ebf89cbf3b222e0b675a55097d # via feast (pyproject.toml) -filelock==3.25.2 \ - --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 \ - --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +filelock==3.29.0 \ + --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ + --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via snowflake-connector-python flit-core==3.12.0 \ --hash=sha256:18f63100d6f94385c6ed57a72073443e1a71a4acb4339491615d0f16d6ff01b2 \ @@ -786,9 +786,9 @@ fsspec==2024.9.0 \ # via # feast (pyproject.toml) # dask -google-api-core[grpc]==2.30.2 \ - --hash=sha256:9a8113e1a88bdc09a7ff629707f2214d98d61c7f6ceb0ea38c42a095d02dc0f9 \ - --hash=sha256:a4c226766d6af2580577db1f1a51bf53cd262f722b49731ce7414c43068a9594 +google-api-core[grpc]==2.30.3 \ + --hash=sha256:a85761ba72c444dad5d611c2220633480b2b6be2521eca69cca2dbb3ffd6bfe8 \ + --hash=sha256:e601a37f148585319b26db36e219df68c5d07b6382cff2d580e83404e44d641b # via # feast (pyproject.toml) # google-cloud-bigquery @@ -798,9 +798,9 @@ google-api-core[grpc]==2.30.2 \ # google-cloud-datastore # google-cloud-storage # pandas-gbq -google-auth==2.49.1 \ - --hash=sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64 \ - --hash=sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7 +google-auth==2.49.2 \ + --hash=sha256:c1ae38500e73065dcae57355adb6278cf8b5c8e391994ae9cbadbcb9631ab409 \ + --hash=sha256:c2720924dfc82dedb962c9f52cabb2ab16714fd0a6a707e40561d217574ed6d5 # via # google-api-core # google-auth-oauthlib @@ -900,60 +900,66 @@ googleapis-common-protos[grpc]==1.74.0 \ # google-api-core # grpc-google-iam-v1 # grpcio-status -greenlet==3.3.2 \ - --hash=sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd \ - --hash=sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082 \ - --hash=sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b \ - --hash=sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5 \ - --hash=sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f \ - --hash=sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727 \ - --hash=sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e \ - --hash=sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2 \ - --hash=sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f \ - --hash=sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327 \ - --hash=sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd \ - --hash=sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2 \ - --hash=sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070 \ - --hash=sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99 \ - --hash=sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be \ - --hash=sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79 \ - --hash=sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7 \ - --hash=sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e \ - --hash=sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf \ - --hash=sha256:5d0e35379f93a6d0222de929a25ab47b5eb35b5ef4721c2b9cbcc4036129ff1f \ - --hash=sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506 \ - --hash=sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a \ - --hash=sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395 \ - --hash=sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4 \ - --hash=sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca \ - --hash=sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492 \ - --hash=sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab \ - --hash=sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358 \ - --hash=sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce \ - --hash=sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5 \ - --hash=sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef \ - --hash=sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d \ - --hash=sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac \ - --hash=sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55 \ - --hash=sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124 \ - --hash=sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4 \ - --hash=sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986 \ - --hash=sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd \ - --hash=sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f \ - --hash=sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb \ - --hash=sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4 \ - --hash=sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13 \ - --hash=sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab \ - --hash=sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff \ - --hash=sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a \ - --hash=sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9 \ - --hash=sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86 \ - --hash=sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd \ - --hash=sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71 \ - --hash=sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92 \ - --hash=sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643 \ - --hash=sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54 \ - --hash=sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9 +greenlet==3.4.0 \ + --hash=sha256:04403ac74fe295a361f650818de93be11b5038a78f49ccfb64d3b1be8fbf1267 \ + --hash=sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2 \ + --hash=sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19 \ + --hash=sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd \ + --hash=sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d \ + --hash=sha256:0e1254cf0cbaa17b04320c3a78575f29f3c161ef38f59c977108f19ffddaf077 \ + --hash=sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82 \ + --hash=sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e \ + --hash=sha256:16dec271460a9a2b154e3b1c2fa1050ce6280878430320e85e08c166772e3f97 \ + --hash=sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705 \ + --hash=sha256:1a54a921561dd9518d31d2d3db4d7f80e589083063ab4d3e2e950756ef809e1a \ + --hash=sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72 \ + --hash=sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d \ + --hash=sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729 \ + --hash=sha256:234582c20af9742583c3b2ddfbdbb58a756cfff803763ffaae1ac7990a9fac31 \ + --hash=sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a \ + --hash=sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1 \ + --hash=sha256:439fc2f12b9b512d9dfa681c5afe5f6b3232c708d13e6f02c845e0d9f4c2d8c6 \ + --hash=sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf \ + --hash=sha256:523677e69cd4711b5a014e37bc1fb3a29947c3e3a5bb6a527e1cc50312e5a398 \ + --hash=sha256:5434271357be07f3ad0936c312645853b7e689e679e29310e2de09a9ea6c3adf \ + --hash=sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1 \ + --hash=sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c \ + --hash=sha256:5cb614ace7c27571270354e9c9f696554d073f8aa9319079dcba466bbdead711 \ + --hash=sha256:636d2f95c309e35f650e421c23297d5011716be15d966e6328b367c9fc513a82 \ + --hash=sha256:6f0def07ec9a71d72315cf26c061aceee53b306c36ed38c35caba952ea1b319d \ + --hash=sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f \ + --hash=sha256:805bebb4945094acbab757d34d6e1098be6de8966009ab9ca54f06ff492def58 \ + --hash=sha256:8424683caf46eb0eb6f626cb95e008e8cc30d0cb675bdfa48200925c79b38a08 \ + --hash=sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940 \ + --hash=sha256:89995ce5ddcd2896d89615116dd39b9703bfa0c07b583b85b89bf1b5d6eddf81 \ + --hash=sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda \ + --hash=sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf \ + --hash=sha256:8c5696c42e6bb5cfb7c6ff4453789081c66b9b91f061e5e9367fa15792644e76 \ + --hash=sha256:90036ce224ed6fe75508c1907a77e4540176dcf0744473627785dd519c6f9996 \ + --hash=sha256:9390ad88b652b1903814eaabd629ca184db15e0eeb6fe8a390bbf8b9106ae15a \ + --hash=sha256:956215d5e355fffa7c021d168728321fd4d31fd730ac609b1653b450f6a4bc71 \ + --hash=sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f \ + --hash=sha256:9b2d9a138ffa0e306d0e2b72976d2fb10b97e690d40ab36a472acaab0838e2de \ + --hash=sha256:a0a53fb071531d003b075c444014ff8f8b1a9898d36bb88abd9ac7b3524648a2 \ + --hash=sha256:a19093fbad824ed7c0f355b5ff4214bffda5f1a7f35f29b31fcaa240cc0135ab \ + --hash=sha256:a1c4f6b453006efb8310affb2d132832e9bbb4fc01ce6df6b70d810d38f1f6dc \ + --hash=sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8 \ + --hash=sha256:a70ed1cb0295bee1df57b63bf7f46b4e56a5c93709eea769c1fec1bb23a95875 \ + --hash=sha256:ac6a5f618be581e1e0713aecec8e54093c235e5fa17d6d8eb7ffc487e2300508 \ + --hash=sha256:b45e45fe47a19051a396abb22e19e7836a59ee6c5a90f3be427343c37908d65b \ + --hash=sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55 \ + --hash=sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa \ + --hash=sha256:c660bce1940a1acae5f51f0a064f1bc785d07ea16efcb4bc708090afc4d69e83 \ + --hash=sha256:d18eae9a7fb0f499efcd146b8c9750a2e1f6e0e93b5a382b3481875354a430e6 \ + --hash=sha256:d336d46878e486de7d9458653c722875547ac8d36a1cff9ffaf4a74a3c1f62eb \ + --hash=sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece \ + --hash=sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed \ + --hash=sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615 \ + --hash=sha256:ee407d4d1ca9dc632265aee1c8732c4a2d60adff848057cdebfe5fe94eb2c8a2 \ + --hash=sha256:f38b81880ba28f232f1f675893a39cf7b6db25b31cc0a09bb50787ecf957e85e \ + --hash=sha256:f50a96b64dafd6169e595a5c56c9146ef80333e67d4476a65a9c55f400fc22ff \ + --hash=sha256:f8296d4e2b92af34ebde81085a01690f26a51eb9ac09a0fcadb331eb36dbc802 \ + --hash=sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf # via feast (pyproject.toml) grpc-google-iam-v1==0.14.4 \ --hash=sha256:392b3796947ed6334e61171d9ab06bf7eb357f554e5fc7556ad7aab6d0e17038 \ @@ -1224,9 +1230,9 @@ ibis-framework[duckdb]==12.0.0 \ --hash=sha256:0bbd790f268da9cb87926d5eaad2b827a573927113c4ed3be5095efa89b9e512 \ --hash=sha256:238624f2c14fdab8382ca2f4f667c3cdb81e29844cd5f8db8a325d0743767c61 # via feast (pyproject.toml) -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx @@ -1264,97 +1270,97 @@ kubernetes==35.0.0 \ --hash=sha256:39e2b33b46e5834ef6c3985ebfe2047ab39135d41de51ce7641a7ca5b372a13d \ --hash=sha256:3d00d344944239821458b9efd484d6df9f011da367ecb155dadf9513f05f09ee # via feast (pyproject.toml) -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -1733,51 +1739,51 @@ multidict==6.7.1 \ # aiobotocore # aiohttp # yarl -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -1867,9 +1873,9 @@ oauthlib==3.3.1 \ --hash=sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9 \ --hash=sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1 # via requests-oauthlib -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # db-dtypes @@ -1949,9 +1955,9 @@ pandas==2.3.3 \ # pandas-gbq # pymilvus # snowflake-connector-python -pandas-gbq==0.34.1 \ - --hash=sha256:6bea5b85937251b976cf9db38151ea59abbff98771179183488d4614694bff67 \ - --hash=sha256:b74932c6ee35dfc81582f39c792e3a68c9ef9bee8c85f25667d9d05dfadd0daf +pandas-gbq==0.35.0 \ + --hash=sha256:258de481019566611031919997bf9c1ece4ca30a4dd02d3fc3664b251d446182 \ + --hash=sha256:596c908487ef0649a161e86ef272c00c267e581b95dc5ee0dc3518545b33bcfc # via google-cloud-bigquery parsy==2.2 \ --hash=sha256:5e981613d9d2d8b68012d1dd0afe928967bea2e4eefdb76c2f545af0dd02a9e7 \ @@ -1972,24 +1978,24 @@ patchelf==0.17.2.4 \ --hash=sha256:d842b51f0401460f3b1f3a3a67d2c266a8f515a5adfbfa6e7b656cb3ac2ed8bc \ --hash=sha256:d9b35ebfada70c02679ad036407d9724ffe1255122ba4ac5e4be5868618a5689 # via feast (pyproject.toml) -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via # hatchling # mypy # scikit-build-core -platformdirs==4.9.4 \ - --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 \ - --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 +platformdirs==4.9.6 \ + --hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \ + --hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917 # via snowflake-connector-python pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 # via hatchling -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) propcache==0.4.1 \ --hash=sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e \ @@ -2185,57 +2191,57 @@ psycopg-pool==3.3.0 \ --hash=sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 \ --hash=sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5 # via psycopg -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask @@ -2264,141 +2270,140 @@ pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi # fastapi-mcp # mcp # pydantic-settings -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic -pydantic-settings==2.13.1 \ - --hash=sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025 \ - --hash=sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237 +pydantic-settings==2.14.0 \ + --hash=sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d \ + --hash=sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e # via # fastapi-mcp # mcp @@ -2428,9 +2433,9 @@ pymysql==1.1.2 \ --hash=sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03 \ --hash=sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9 # via feast (pyproject.toml) -pyopenssl==26.0.0 \ - --hash=sha256:df94d28498848b98cc1c0ffb8ef1e71e40210d3b0a8064c9d29571ed2904bf81 \ - --hash=sha256:f293934e52936f2e3413b89c6ce36df66a0b34ae1ea3a053b8c5020ff2f513fc +pyopenssl==26.1.0 \ + --hash=sha256:115563879b2c8ccb207975705d3e491434d8c9d7c79667c902ecbf5f3bbd2ece \ + --hash=sha256:737f0a2275c5bc54f3b02137687e1a765931fb3949b9a92a825e4d33b9eec08b # via snowflake-connector-python pyproject-metadata==0.11.0 \ --hash=sha256:85bbecca8694e2c00f63b492c96921d6c228454057c88e7c352b2077fcaa4096 \ @@ -2453,9 +2458,9 @@ python-dotenv==1.2.2 \ # pydantic-settings # pymilvus # uvicorn -python-multipart==0.0.22 \ - --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \ - --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58 +python-multipart==0.0.26 \ + --hash=sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17 \ + --hash=sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185 # via mcp pytz==2026.1.post1 \ --hash=sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1 \ @@ -2571,9 +2576,9 @@ requests-oauthlib==2.0.0 \ # via # google-auth-oauthlib # kubernetes -rich==14.3.3 \ - --hash=sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d \ - --hash=sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 # via # fastapi-mcp # ibis-framework @@ -2795,74 +2800,74 @@ sphinxcontrib-serializinghtml==2.0.0 \ --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 \ --hash=sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d # via sphinx -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) -sqlglot==30.2.1 \ - --hash=sha256:ef4a67cc6f66a8043085eb8ea95fa9541c1625dffa9145ad4e9815a7ba60a199 \ - --hash=sha256:f23d9ee9427ef9d20df15f9b0ffa57d9eb45e52b012219a349d1e6b50ed926d1 +sqlglot==30.6.0 \ + --hash=sha256:246d34d39927422a50a3fa155f37b2f6346fba85f1a755b13c941eb32ef93361 \ + --hash=sha256:e005fc2f47994f90d7d8df341f1cbe937518497b0b7b1507d4c03c4c9dfd2778 # via ibis-framework sse-starlette==3.3.4 \ --hash=sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1 \ @@ -2961,17 +2966,17 @@ typeguard==4.5.1 \ --hash=sha256:44d2bf329d49a244110a090b55f5f91aa82d9a9834ebfd30bcc73651e4a8cc40 \ --hash=sha256:f6f8ecbbc819c9bc749983cc67c02391e16a9b43b8b27f15dc70ed7c4a007274 # via feast (pyproject.toml) -typer==0.24.1 \ - --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e \ - --hash=sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45 +typer==0.24.2 \ + --hash=sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8 \ + --hash=sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480 # via fastapi-mcp types-psutil==7.0.0.20250218 \ --hash=sha256:1447a30c282aafefcf8941ece854e1100eee7b0296a9d9be9977292f0269b121 \ --hash=sha256:1e642cdafe837b240295b23b1cbd4691d80b08a07d29932143cbbae30eb0db9c # via feast (pyproject.toml) -types-pymysql==1.1.0.20251220 \ - --hash=sha256:ae1c3df32a777489431e2e9963880a0df48f6591e0aa2fd3a6fabd9dee6eca54 \ - --hash=sha256:fa1082af7dea6c53b6caa5784241924b1296ea3a8d3bd060417352c5e10c0618 +types-pymysql==1.1.0.20260408 \ + --hash=sha256:b784dc37908479e3767e2d794ab507b3674adb1c686ca3d13fc9e2960dbcb9ec \ + --hash=sha256:da630647eaaa7a926a3907794f4067f269cd245b2c202c74aa3c6a3bd660a9db # via feast (pyproject.toml) typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ @@ -3002,9 +3007,9 @@ typing-inspection==0.4.2 \ # mcp # pydantic # pydantic-settings -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via # ibis-framework # pandas diff --git a/sdk/python/requirements/py3.12-requirements.txt b/sdk/python/requirements/py3.12-requirements.txt index bd0810289bc..f5d0f5ecef7 100644 --- a/sdk/python/requirements/py3.12-requirements.txt +++ b/sdk/python/requirements/py3.12-requirements.txt @@ -24,9 +24,9 @@ bigtree==1.4.0 \ --hash=sha256:d0d99550ae64ce4529f132602ab875c2ab472c96c942f5704f8c72a17450d3ea \ --hash=sha256:e5ae2e948168da671d99601c9ed87ab3b48d9d4ea8a98f111e5748e98064c31c # via feast (pyproject.toml) -certifi==2026.2.25 \ - --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ - --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 +certifi==2026.4.22 \ + --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ + --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via requests charset-normalizer==3.4.7 \ --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \ @@ -159,9 +159,9 @@ charset-normalizer==3.4.7 \ --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \ --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464 # via requests -click==8.3.1 \ - --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \ - --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 +click==8.3.3 \ + --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ + --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 # via # feast (pyproject.toml) # dask @@ -182,9 +182,9 @@ dill==0.3.9 \ --hash=sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a \ --hash=sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c # via feast (pyproject.toml) -fastapi==0.135.3 \ - --hash=sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98 \ - --hash=sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654 +fastapi==0.136.1 \ + --hash=sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f \ + --hash=sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f # via feast (pyproject.toml) fsspec==2026.3.0 \ --hash=sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41 \ @@ -245,9 +245,9 @@ httptools==0.7.1 \ --hash=sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec \ --hash=sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362 # via uvicorn -idna==3.11 \ - --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ - --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 +idna==3.13 \ + --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ + --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # requests @@ -263,97 +263,97 @@ jsonschema-specifications==2025.9.1 \ --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d # via jsonschema -librt==0.8.1 \ - --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ - --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ - --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ - --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ - --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ - --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ - --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ - --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ - --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ - --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ - --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ - --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ - --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ - --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ - --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ - --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ - --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ - --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ - --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ - --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ - --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ - --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ - --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ - --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ - --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ - --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ - --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ - --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ - --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ - --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ - --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ - --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ - --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ - --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ - --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ - --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ - --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ - --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ - --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ - --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ - --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ - --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ - --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ - --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ - --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ - --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ - --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ - --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ - --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ - --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ - --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ - --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ - --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ - --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ - --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ - --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ - --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ - --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ - --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ - --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ - --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ - --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ - --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ - --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ - --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ - --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ - --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ - --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 \ - --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ - --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ - --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ - --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ - --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ - --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ - --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ - --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ - --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ - --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ - --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ - --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ - --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ - --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ - --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ - --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ - --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ - --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ - --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ - --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ - --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ - --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 +librt==0.9.0 \ + --hash=sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d \ + --hash=sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d \ + --hash=sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38 \ + --hash=sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8 \ + --hash=sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a \ + --hash=sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb \ + --hash=sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6 \ + --hash=sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499 \ + --hash=sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2 \ + --hash=sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285 \ + --hash=sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5 \ + --hash=sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0 \ + --hash=sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6 \ + --hash=sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443 \ + --hash=sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b \ + --hash=sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745 \ + --hash=sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb \ + --hash=sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228 \ + --hash=sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f \ + --hash=sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c \ + --hash=sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845 \ + --hash=sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5 \ + --hash=sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c \ + --hash=sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f \ + --hash=sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4 \ + --hash=sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54 \ + --hash=sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1 \ + --hash=sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236 \ + --hash=sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f \ + --hash=sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27 \ + --hash=sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b \ + --hash=sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc \ + --hash=sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858 \ + --hash=sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f \ + --hash=sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b \ + --hash=sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938 \ + --hash=sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a \ + --hash=sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b \ + --hash=sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d \ + --hash=sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f \ + --hash=sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71 \ + --hash=sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22 \ + --hash=sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8 \ + --hash=sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990 \ + --hash=sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f \ + --hash=sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2 \ + --hash=sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd \ + --hash=sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076 \ + --hash=sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671 \ + --hash=sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9 \ + --hash=sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15 \ + --hash=sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4 \ + --hash=sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f \ + --hash=sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8 \ + --hash=sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d \ + --hash=sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265 \ + --hash=sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61 \ + --hash=sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519 \ + --hash=sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a \ + --hash=sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40 \ + --hash=sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7 \ + --hash=sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f \ + --hash=sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e \ + --hash=sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9 \ + --hash=sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a \ + --hash=sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3 \ + --hash=sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee \ + --hash=sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11 \ + --hash=sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4 \ + --hash=sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283 \ + --hash=sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15 \ + --hash=sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084 \ + --hash=sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e \ + --hash=sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882 \ + --hash=sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f \ + --hash=sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e \ + --hash=sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774 \ + --hash=sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce \ + --hash=sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927 \ + --hash=sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6 \ + --hash=sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118 \ + --hash=sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4 \ + --hash=sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e \ + --hash=sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1 \ + --hash=sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f \ + --hash=sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2 \ + --hash=sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856 \ + --hash=sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1 \ + --hash=sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f \ + --hash=sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a # via mypy locket==1.0.0 \ --hash=sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632 \ @@ -559,51 +559,51 @@ mmh3==5.2.1 \ --hash=sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a \ --hash=sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b # via feast (pyproject.toml) -mypy==1.20.0 \ - --hash=sha256:002b613ae19f4ac7d18b7e168ffe1cb9013b37c57f7411984abbd3b817b0a214 \ - --hash=sha256:00e047c74d3ec6e71a2eb88e9ea551a2edb90c21f993aefa9e0d2a898e0bb732 \ - --hash=sha256:02cca0761c75b42a20a2757ae58713276605eb29a08dd8a6e092aa347c4115ca \ - --hash=sha256:0ecd63f75fdd30327e4ad8b5704bd6d91fc6c1b2e029f8ee14705e1207212489 \ - --hash=sha256:0f42dfaab7ec1baff3b383ad7af562ab0de573c5f6edb44b2dab016082b89948 \ - --hash=sha256:1973868d2adbb4584a3835780b27436f06d1dc606af5be09f187aaa25be1070f \ - --hash=sha256:26c8b52627b6552f47ff11adb4e1509605f094e29815323e487fc0053ebe93d1 \ - --hash=sha256:2721f0ce49cb74a38f00c50da67cb7d36317b5eda38877a49614dc018e91c787 \ - --hash=sha256:2fcedb16d456106e545b2bfd7ef9d24e70b38ec252d2a629823a4d07ebcdb69e \ - --hash=sha256:31b5dbb55293c1bd27c0fc813a0d2bb5ceef9d65ac5afa2e58f829dab7921fd5 \ - --hash=sha256:34506397dbf40c15dc567635d18a21d33827e9ab29014fb83d292a8f4f8953b6 \ - --hash=sha256:367e5c993ba34d5054d11937d0485ad6dfc60ba760fa326c01090fc256adf15c \ - --hash=sha256:379edf079ce44ac8d2805bcf9b3dd7340d4f97aad3a5e0ebabbf9d125b84b442 \ - --hash=sha256:39362cdb4ba5f916e7976fccecaab1ba3a83e35f60fa68b64e9a70e221bb2436 \ - --hash=sha256:4525e7010b1b38334516181c5b81e16180b8e149e6684cee5a727c78186b4e3b \ - --hash=sha256:47781555a7aa5fedcc2d16bcd72e0dc83eb272c10dd657f9fb3f9cc08e2e6abb \ - --hash=sha256:49d11c6f573a5a08f77fad13faff2139f6d0730ebed2cfa9b3d2702671dd7188 \ - --hash=sha256:555493c44a4f5a1b58d611a43333e71a9981c6dbe26270377b6f8174126a0526 \ - --hash=sha256:555658c611099455b2da507582ea20d2043dfdfe7f5ad0add472b1c6238b433f \ - --hash=sha256:697f102c5c1d526bdd761a69f17c6070f9892eebcb94b1a5963d679288c09e78 \ - --hash=sha256:76a70bf840495729be47510856b978f1b0ec7d08f257ca38c9d932720bf6b43e \ - --hash=sha256:7d3243c406773185144527f83be0e0aefc7bf4601b0b2b956665608bf7c98a83 \ - --hash=sha256:931a7630bba591593dcf6e97224a21ff80fb357e7982628d25e3c618e7f598ef \ - --hash=sha256:9804c3ad27f78e54e58b32e7cb532d128b43dbfb9f3f9f06262b821a0f6bd3f5 \ - --hash=sha256:a17c5d0bdcca61ce24a35beb828a2d0d323d3fcf387d7512206888c900193367 \ - --hash=sha256:a6e0641147cbfa7e4e94efdb95c2dab1aff8cfc159ded13e07f308ddccc8c48e \ - --hash=sha256:a79c1eba7ac4209f2d850f0edd0a2f8bba88cbfdfefe6fb76a19e9d4fe5e71a2 \ - --hash=sha256:a9336b5e6712f4adaf5afc3203a99a40b379049104349d747eb3e5a3aa23ac2e \ - --hash=sha256:b20c8b0fd5877abdf402e79a3af987053de07e6fb208c18df6659f708b535134 \ - --hash=sha256:b3a49064504be59e59da664c5e149edc1f26c67c4f8e8456f6ba6aba55033018 \ - --hash=sha256:b503ab55a836136b619b5fc21c8803d810c5b87551af8600b72eecafb0059cb0 \ - --hash=sha256:bd0212976dc57a5bfeede7c219e7cd66568a32c05c9129686dd487c059c1b88a \ - --hash=sha256:c70380fe5d64010f79fb863b9081c7004dd65225d2277333c219d93a10dad4dd \ - --hash=sha256:d99f515f95fd03a90875fdb2cca12ff074aa04490db4d190905851bdf8a549a8 \ - --hash=sha256:e80cf77847d0d3e6e3111b7b25db32a7f8762fd4b9a3a72ce53fe16a2863b281 \ - --hash=sha256:eb96c84efcc33f0b5e0e04beacf00129dd963b67226b01c00b9dfc8affb464c3 \ - --hash=sha256:ebea00201737ad4391142808ed16e875add5c17f676e0912b387739f84991e13 \ - --hash=sha256:efe8d70949c3023698c3fca1e94527e7e790a361ab8116f90d11221421cd8726 \ - --hash=sha256:f13b3e41bce9d257eded794c0f12878af3129d80aacd8a3ee0dee51f3a978651 \ - --hash=sha256:f194db59657c58593a3c47c6dfd7bad4ef4ac12dbc94d01b3a95521f78177e33 \ - --hash=sha256:f49590891d2c2f8a9de15614e32e459a794bcba84693c2394291a2038bbaaa69 \ - --hash=sha256:f75ff57defcd0f1d6e006d721ccdec6c88d4f6a7816eb92f1c4890d979d9ee62 \ - --hash=sha256:f799d9db89fc00446f03281f84a221e50018fc40113a3ba9864b132895619ebe \ - --hash=sha256:f8426d4d75d68714abc17a4292d922f6ba2cfb984b72c2278c437f6dae797865 +mypy==1.20.2 \ + --hash=sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb \ + --hash=sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98 \ + --hash=sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99 \ + --hash=sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100 \ + --hash=sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744 \ + --hash=sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f \ + --hash=sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609 \ + --hash=sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6 \ + --hash=sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c \ + --hash=sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30 \ + --hash=sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4 \ + --hash=sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b \ + --hash=sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558 \ + --hash=sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2 \ + --hash=sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102 \ + --hash=sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc \ + --hash=sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c \ + --hash=sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2 \ + --hash=sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517 \ + --hash=sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58 \ + --hash=sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997 \ + --hash=sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6 \ + --hash=sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c \ + --hash=sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8 \ + --hash=sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14 \ + --hash=sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec \ + --hash=sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee \ + --hash=sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066 \ + --hash=sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563 \ + --hash=sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330 \ + --hash=sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67 \ + --hash=sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15 \ + --hash=sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac \ + --hash=sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3 \ + --hash=sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254 \ + --hash=sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3 \ + --hash=sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4 \ + --hash=sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9 \ + --hash=sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382 \ + --hash=sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943 \ + --hash=sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924 \ + --hash=sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665 \ + --hash=sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026 \ + --hash=sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd # via sqlalchemy mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ @@ -686,9 +686,9 @@ numpy==2.4.4 \ # feast (pyproject.toml) # dask # pandas -packaging==26.0 \ - --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \ - --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # dask # gunicorn @@ -755,13 +755,13 @@ partd==1.4.2 \ --hash=sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f \ --hash=sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c # via dask -pathspec==1.0.4 \ - --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \ - --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +pathspec==1.1.0 \ + --hash=sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42 \ + --hash=sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080 # via mypy -prometheus-client==0.24.1 \ - --hash=sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055 \ - --hash=sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9 +prometheus-client==0.25.0 \ + --hash=sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28 \ + --hash=sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1 # via feast (pyproject.toml) protobuf==7.34.1 \ --hash=sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a \ @@ -796,188 +796,187 @@ psutil==7.2.2 \ --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 # via feast (pyproject.toml) -pyarrow==23.0.1 \ - --hash=sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07 \ - --hash=sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0 \ - --hash=sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350 \ - --hash=sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb \ - --hash=sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d \ - --hash=sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9 \ - --hash=sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1 \ - --hash=sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500 \ - --hash=sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5 \ - --hash=sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701 \ - --hash=sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c \ - --hash=sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56 \ - --hash=sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7 \ - --hash=sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1 \ - --hash=sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce \ - --hash=sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730 \ - --hash=sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d \ - --hash=sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2 \ - --hash=sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca \ - --hash=sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f \ - --hash=sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8 \ - --hash=sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb \ - --hash=sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125 \ - --hash=sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 \ - --hash=sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f \ - --hash=sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7 \ - --hash=sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05 \ - --hash=sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9 \ - --hash=sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f \ - --hash=sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2 \ - --hash=sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37 \ - --hash=sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690 \ - --hash=sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8 \ - --hash=sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814 \ - --hash=sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019 \ - --hash=sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67 \ - --hash=sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83 \ - --hash=sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886 \ - --hash=sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2 \ - --hash=sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41 \ - --hash=sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a \ - --hash=sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258 \ - --hash=sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78 \ - --hash=sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5 \ - --hash=sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d \ - --hash=sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222 \ - --hash=sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919 \ - --hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \ - --hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \ - --hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd +pyarrow==24.0.0 \ + --hash=sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba \ + --hash=sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68 \ + --hash=sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868 \ + --hash=sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495 \ + --hash=sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e \ + --hash=sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66 \ + --hash=sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275 \ + --hash=sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3 \ + --hash=sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838 \ + --hash=sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491 \ + --hash=sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6 \ + --hash=sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826 \ + --hash=sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a \ + --hash=sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981 \ + --hash=sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e \ + --hash=sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c \ + --hash=sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e \ + --hash=sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05 \ + --hash=sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b \ + --hash=sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb \ + --hash=sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136 \ + --hash=sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810 \ + --hash=sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37 \ + --hash=sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147 \ + --hash=sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0 \ + --hash=sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b \ + --hash=sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb \ + --hash=sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f \ + --hash=sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83 \ + --hash=sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca \ + --hash=sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931 \ + --hash=sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d \ + --hash=sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2 \ + --hash=sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795 \ + --hash=sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26 \ + --hash=sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74 \ + --hash=sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c \ + --hash=sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57 \ + --hash=sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1 \ + --hash=sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591 \ + --hash=sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19 \ + --hash=sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42 \ + --hash=sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde \ + --hash=sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699 \ + --hash=sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041 \ + --hash=sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91 \ + --hash=sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76 \ + --hash=sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b \ + --hash=sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a \ + --hash=sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072 # via # feast (pyproject.toml) # dask -pydantic==2.12.5 \ - --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \ - --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d +pydantic==2.13.3 \ + --hash=sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927 \ + --hash=sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d # via # feast (pyproject.toml) # fastapi -pydantic-core==2.41.5 \ - --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \ - --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \ - --hash=sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504 \ - --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \ - --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \ - --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \ - --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \ - --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \ - --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \ - --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \ - --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \ - --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \ - --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \ - --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \ - --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \ - --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \ - --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \ - --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \ - --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \ - --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \ - --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \ - --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \ - --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \ - --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \ - --hash=sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5 \ - --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \ - --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \ - --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \ - --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \ - --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \ - --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \ - --hash=sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5 \ - --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \ - --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \ - --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \ - --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \ - --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \ - --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \ - --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \ - --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \ - --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \ - --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \ - --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \ - --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \ - --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \ - --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \ - --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \ - --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \ - --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \ - --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \ - --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \ - --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \ - --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \ - --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \ - --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \ - --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \ - --hash=sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3 \ - --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \ - --hash=sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3 \ - --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \ - --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \ - --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \ - --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \ - --hash=sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60 \ - --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \ - --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \ - --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \ - --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \ - --hash=sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460 \ - --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \ - --hash=sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf \ - --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \ - --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \ - --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \ - --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \ - --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \ - --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \ - --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \ - --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \ - --hash=sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d \ - --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \ - --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \ - --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \ - --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \ - --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \ - --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \ - --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \ - --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \ - --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \ - --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \ - --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \ - --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \ - --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \ - --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \ - --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \ - --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \ - --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \ - --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \ - --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \ - --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \ - --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \ - --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \ - --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \ - --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \ - --hash=sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b \ - --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \ - --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \ - --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \ - --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \ - --hash=sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82 \ - --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \ - --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \ - --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \ - --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \ - --hash=sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5 \ - --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \ - --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \ - --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \ - --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \ - --hash=sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425 \ - --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52 +pydantic-core==2.46.3 \ + --hash=sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba \ + --hash=sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35 \ + --hash=sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4 \ + --hash=sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505 \ + --hash=sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7 \ + --hash=sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8 \ + --hash=sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4 \ + --hash=sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37 \ + --hash=sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25 \ + --hash=sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022 \ + --hash=sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1 \ + --hash=sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7 \ + --hash=sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c \ + --hash=sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3 \ + --hash=sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb \ + --hash=sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3 \ + --hash=sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976 \ + --hash=sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c \ + --hash=sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab \ + --hash=sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa \ + --hash=sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6 \ + --hash=sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396 \ + --hash=sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c \ + --hash=sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495 \ + --hash=sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c \ + --hash=sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0 \ + --hash=sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd \ + --hash=sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf \ + --hash=sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531 \ + --hash=sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5 \ + --hash=sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda \ + --hash=sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d \ + --hash=sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e \ + --hash=sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df \ + --hash=sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6 \ + --hash=sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c \ + --hash=sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13 \ + --hash=sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536 \ + --hash=sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287 \ + --hash=sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0 \ + --hash=sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720 \ + --hash=sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050 \ + --hash=sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c \ + --hash=sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1 \ + --hash=sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8 \ + --hash=sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0 \ + --hash=sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374 \ + --hash=sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807 \ + --hash=sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6 \ + --hash=sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873 \ + --hash=sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57 \ + --hash=sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f \ + --hash=sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c \ + --hash=sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad \ + --hash=sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e \ + --hash=sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd \ + --hash=sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23 \ + --hash=sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46 \ + --hash=sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1 \ + --hash=sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d \ + --hash=sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1 \ + --hash=sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee \ + --hash=sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c \ + --hash=sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874 \ + --hash=sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168 \ + --hash=sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a \ + --hash=sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13 \ + --hash=sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f \ + --hash=sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a \ + --hash=sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789 \ + --hash=sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe \ + --hash=sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f \ + --hash=sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5 \ + --hash=sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943 \ + --hash=sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b \ + --hash=sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 \ + --hash=sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b \ + --hash=sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff \ + --hash=sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67 \ + --hash=sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803 \ + --hash=sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045 \ + --hash=sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8 \ + --hash=sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 \ + --hash=sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2 \ + --hash=sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f \ + --hash=sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687 \ + --hash=sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76 \ + --hash=sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 \ + --hash=sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f \ + --hash=sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 \ + --hash=sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c \ + --hash=sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 \ + --hash=sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba \ + --hash=sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c \ + --hash=sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf \ + --hash=sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7 \ + --hash=sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47 \ + --hash=sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6 \ + --hash=sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79 \ + --hash=sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a \ + --hash=sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e \ + --hash=sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a \ + --hash=sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34 \ + --hash=sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b \ + --hash=sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85 \ + --hash=sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca \ + --hash=sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f \ + --hash=sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3 \ + --hash=sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64 \ + --hash=sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22 \ + --hash=sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72 \ + --hash=sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec \ + --hash=sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d \ + --hash=sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3 \ + --hash=sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb \ + --hash=sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 \ + --hash=sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb \ + --hash=sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4 \ + --hash=sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127 \ + --hash=sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56 # via pydantic pygments==2.20.0 \ --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ @@ -1210,70 +1209,70 @@ six==1.17.0 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 # via python-dateutil -sqlalchemy[mypy]==2.0.48 \ - --hash=sha256:01f6bbd4308b23240cf7d3ef117557c8fd097ec9549d5d8a52977544e35b40ad \ - --hash=sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e \ - --hash=sha256:10853a53a4a00417a00913d270dddda75815fcb80675874285f41051c094d7dd \ - --hash=sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6 \ - --hash=sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0 \ - --hash=sha256:1a89ce07ad2d4b8cfc30bd5889ec40613e028ed80ef47da7d9dd2ce969ad30e0 \ - --hash=sha256:1b4c575df7368b3b13e0cebf01d4679f9a28ed2ae6c1cd0b1d5beffb6b2007dc \ - --hash=sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b \ - --hash=sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f \ - --hash=sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0 \ - --hash=sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894 \ - --hash=sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b \ - --hash=sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8 \ - --hash=sha256:3713e21ea67bca727eecd4a24bf68bcd414c403faae4989442be60994301ded0 \ - --hash=sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131 \ - --hash=sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b \ - --hash=sha256:4599a95f9430ae0de82b52ff0d27304fe898c17cb5f4099f7438a51b9998ac77 \ - --hash=sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f \ - --hash=sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb \ - --hash=sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9 \ - --hash=sha256:583849c743e0e3c9bb7446f5b5addeacedc168d657a69b418063dfdb2d90081c \ - --hash=sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241 \ - --hash=sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658 \ - --hash=sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7 \ - --hash=sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a \ - --hash=sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae \ - --hash=sha256:6bb85c546591569558571aa1b06aba711b26ae62f111e15e56136d69920e1616 \ - --hash=sha256:6f7b7243850edd0b8b97043f04748f31de50cf426e939def5c16bedb540698f7 \ - --hash=sha256:7001dc9d5f6bb4deb756d5928eaefe1930f6f4179da3924cbd95ee0e9f4dce89 \ - --hash=sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3 \ - --hash=sha256:7c998f2ace8bf76b453b75dbcca500d4f4b9dd3908c13e89b86289b37784848b \ - --hash=sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0 \ - --hash=sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2 \ - --hash=sha256:82745b03b4043e04600a6b665cb98697c4339b24e34d74b0a2ac0a2488b6f94d \ - --hash=sha256:841a94c66577661c1f088ac958cd767d7c9bf507698f45afffe7a4017049de76 \ - --hash=sha256:858e433f12b0e5b3ed2f8da917433b634f4937d0e8793e5cb33c54a1a01df565 \ - --hash=sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99 \ - --hash=sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485 \ - --hash=sha256:9c7d0a77e36b5f4b01ca398482230ab792061d243d715299b44a0b55c89fe617 \ - --hash=sha256:a5b429eb84339f9f05e06083f119ad814e6d85e27ecbdf9c551dfdbb128eaf8a \ - --hash=sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096 \ - --hash=sha256:a6b764fb312bd35e47797ad2e63f0d323792837a6ac785a4ca967019357d2bc7 \ - --hash=sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed \ - --hash=sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f \ - --hash=sha256:b8fc3454b4f3bd0a368001d0e968852dad45a873f8b4babd41bc302ec851a099 \ - --hash=sha256:bcb8ebbf2e2c36cfe01a94f2438012c6a9d494cf80f129d9753bcdf33bfc35a6 \ - --hash=sha256:d404dc897ce10e565d647795861762aa2d06ca3f4a728c5e9a835096c7059018 \ - --hash=sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2 \ - --hash=sha256:d64177f443594c8697369c10e4bbcac70ef558e0f7921a1de7e4a3d1734bcf67 \ - --hash=sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933 \ - --hash=sha256:d8fcccbbc0c13c13702c471da398b8cd72ba740dca5859f148ae8e0e8e0d3e7e \ - --hash=sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b \ - --hash=sha256:e214d546c8ecb5fc22d6e6011746082abf13a9cf46eefb45769c7b31407c97b5 \ - --hash=sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd \ - --hash=sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79 \ - --hash=sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 \ - --hash=sha256:e5e088bf43f6ee6fec7dbf1ef7ff7774a616c236b5c0cb3e00662dd71a56b571 \ - --hash=sha256:e83e3f959aaa1c9df95c22c528096d94848a1bc819f5d0ebf7ee3df0ca63db6c \ - --hash=sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121 \ - --hash=sha256:f27f9da0a7d22b9f981108fd4b62f8b5743423388915a563e651c20d06c1f457 \ - --hash=sha256:f8649a14caa5f8a243628b1d61cf530ad9ae4578814ba726816adb1121fc493e \ - --hash=sha256:fac0fa4e4f55f118fd87177dacb1c6522fe39c28d498d259014020fec9164c29 \ - --hash=sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb +sqlalchemy[mypy]==2.0.49 \ + --hash=sha256:01146546d84185f12721a1d2ce0c6673451a7894d1460b592d378ca4871a0c72 \ + --hash=sha256:059d7151fff513c53a4638da8778be7fce81a0c4854c7348ebd0c4078ddf28fe \ + --hash=sha256:0c98c59075b890df8abfcc6ad632879540f5791c68baebacb4f833713b510e75 \ + --hash=sha256:0f2fa354ba106eafff2c14b0cc51f22801d1e8b2e4149342023bd6f0955de5f5 \ + --hash=sha256:12b04d1db2663b421fe072d638a138460a51d5a862403295671c4f3987fb9148 \ + --hash=sha256:22d8798819f86720bc646ab015baff5ea4c971d68121cb36e2ebc2ee43ead2b7 \ + --hash=sha256:233088b4b99ebcbc5258c755a097aa52fbf90727a03a5a80781c4b9c54347a2e \ + --hash=sha256:24bd94bb301ec672d8f0623eba9226cc90d775d25a0c92b5f8e4965d7f3a1518 \ + --hash=sha256:275424295f4256fd301744b8f335cff367825d270f155d522b30c7bf49903ee7 \ + --hash=sha256:32fe6a41ad97302db2931f05bb91abbcc65b5ce4c675cd44b972428dd2947700 \ + --hash=sha256:334edbcff10514ad1d66e3a70b339c0a29886394892490119dbb669627b17717 \ + --hash=sha256:3bb9ec6436a820a4c006aad1ac351f12de2f2dbdaad171692ee457a02429b672 \ + --hash=sha256:3ddcb27fb39171de36e207600116ac9dfd4ae46f86c82a9bf3934043e80ebb88 \ + --hash=sha256:42e8804962f9e6f4be2cbaedc0c3718f08f60a16910fa3d86da5a1e3b1bfe60f \ + --hash=sha256:43d044780732d9e0381ac8d5316f95d7f02ef04d6e4ef6dc82379f09795d993f \ + --hash=sha256:46796877b47034b559a593d7e4b549aba151dae73f9e78212a3478161c12ab08 \ + --hash=sha256:46d51518d53edfbe0563662c96954dc8fcace9832332b914375f45a99b77cc9a \ + --hash=sha256:47604cb2159f8bbd5a1ab48a714557156320f20871ee64d550d8bf2683d980d3 \ + --hash=sha256:4bbccb45260e4ff1b7db0be80a9025bb1e6698bdb808b83fff0000f7a90b2c0b \ + --hash=sha256:4d4e5a0ceba319942fa6b585cf82539288a61e314ef006c1209f734551ab9536 \ + --hash=sha256:55250fe61d6ebfd6934a272ee16ef1244e0f16b7af6cd18ab5b1fc9f08631db0 \ + --hash=sha256:566df36fd0e901625523a5a1835032f1ebdd7f7886c54584143fa6c668b4df3b \ + --hash=sha256:57ca426a48eb2c682dae8204cd89ea8ab7031e2675120a47924fabc7caacbc2a \ + --hash=sha256:5e61abbec255be7b122aa461021daa7c3f310f3e743411a67079f9b3cc91ece3 \ + --hash=sha256:618a308215b6cececb6240b9abde545e3acdabac7ae3e1d4e666896bf5ba44b4 \ + --hash=sha256:62557958002b69699bdb7f5137c6714ca1133f045f97b3903964f47db97ea339 \ + --hash=sha256:6270d717b11c5476b0cbb21eedc8d4dbb7d1a956fd6c15a23e96f197a6193158 \ + --hash=sha256:685e93e9c8f399b0c96a624799820176312f5ceef958c0f88215af4013d29066 \ + --hash=sha256:69469ce8ce7a8df4d37620e3163b71238719e1e2e5048d114a1b6ce0fbf8c662 \ + --hash=sha256:6eb188b84269f357669b62cb576b5b918de10fb7c728a005fa0ebb0b758adce1 \ + --hash=sha256:74ab4ee7794d7ed1b0c37e7333640e0f0a626fc7b398c07a7aef52f484fddde3 \ + --hash=sha256:77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5 \ + --hash=sha256:7c821c47ecfe05cc32140dcf8dc6fd5d21971c86dbd56eabfe5ba07a64910c01 \ + --hash=sha256:7d6be30b2a75362325176c036d7fb8d19e8846c77e87683ffaa8177b35135613 \ + --hash=sha256:7f605a456948c35260e7b2a39f8952a26f077fd25653c37740ed186b90aaa68a \ + --hash=sha256:83101a6930332b87653886c01d1ee7e294b1fe46a07dd9a2d2b4f91bcc88eec0 \ + --hash=sha256:88690f4e1f0fbf5339bedbb127e240fec1fd3070e9934c0b7bef83432f779d2f \ + --hash=sha256:8a97ac839c2c6672c4865e48f3cbad7152cee85f4233fb4ca6291d775b9b954a \ + --hash=sha256:8d6efc136f44a7e8bc8088507eaabbb8c2b55b3dbb63fe102c690da0ddebe55e \ + --hash=sha256:8e20e511dc15265fb433571391ba313e10dd8ea7e509d51686a51313b4ac01a2 \ + --hash=sha256:951d4a210744813be63019f3df343bf233b7432aadf0db54c75802247330d3af \ + --hash=sha256:9ac7a3e245fd0310fd31495eb61af772e637bdf7d88ee81e7f10a3f271bff014 \ + --hash=sha256:9b1c058c171b739e7c330760044803099c7fff11511e3ab3573e5327116a9c33 \ + --hash=sha256:9c04bff9a5335eb95c6ecf1c117576a0aa560def274876fd156cfe5510fccc61 \ + --hash=sha256:9c4969a86e41454f2858256c39bdfb966a20961e9b58bf8749b65abf447e9a8d \ + --hash=sha256:9e0400fa22f79acc334d9a6b185dc00a44a8e6578aa7e12d0ddcd8434152b187 \ + --hash=sha256:a05977bffe9bffd2229f477fa75eabe3192b1b05f408961d1bebff8d1cd4d401 \ + --hash=sha256:a143af2ea6672f2af3f44ed8f9cd020e9cc34c56f0e8db12019d5d9ecf41cb3b \ + --hash=sha256:a51d3db74ba489266ef55c7a4534eb0b8db9a326553df481c11e5d7660c8364d \ + --hash=sha256:b95b2f470c1b2683febd2e7eab1d3f0e078c91dbdd0b00e9c645d07a413bb99f \ + --hash=sha256:b9870d15ef00e4d0559ae10ee5bc71b654d1f20076dbe8bc7ed19b4c0625ceba \ + --hash=sha256:c1dc3368794d522f43914e03312202523cc89692f5389c32bea0233924f8d977 \ + --hash=sha256:c338ec6ec01c0bc8e735c58b9f5d51e75bacb6ff23296658826d7cfdfdb8678a \ + --hash=sha256:c5070135e1b7409c4161133aa525419b0062088ed77c92b1da95366ec5cbebbe \ + --hash=sha256:cc992c6ed024c8c3c592c5fc9846a03dd68a425674900c70122c77ea16c5fb0b \ + --hash=sha256:d15950a57a210e36dd4cec1aac22787e2a4d57ba9318233e2ef8b2daf9ff2d5f \ + --hash=sha256:d898cc2c76c135ef65517f4ddd7a3512fb41f23087b0650efb3418b8389a3cd1 \ + --hash=sha256:d99945830a6f3e9638d89a28ed130b1eb24c91255e4f24366fbe699b983f29e4 \ + --hash=sha256:da9b91bca419dc9b9267ffadde24eae9b1a6bffcd09d0a207e5e3af99a03ce0d \ + --hash=sha256:df2d441bacf97022e81ad047e1597552eb3f83ca8a8f1a1fdd43cd7fe3898120 \ + --hash=sha256:e06e617e3d4fd9e51d385dfe45b077a41e9d1b033a7702551e3278ac597dc750 \ + --hash=sha256:ec44cfa7ef1a728e88ad41674de50f6db8cfdb3e2af84af86e0041aaf02d43d0 \ + --hash=sha256:fb37f15714ec2652d574f021d479e78cd4eb9d04396dca36568fdfffb3487982 # via feast (pyproject.toml) starlette==1.0.0 \ --hash=sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149 \ @@ -1325,9 +1324,9 @@ typing-inspection==0.4.2 \ # via # fastapi # pydantic -tzdata==2025.3 \ - --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \ - --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7 +tzdata==2026.2 \ + --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \ + --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7 # via pandas urllib3==2.6.3 \ --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ From 2905b80eae273325b4b340df15c57a2aa231d256 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 18:04:52 -0400 Subject: [PATCH 65/76] Remove MongoDB from universal test parametrization The MongoDB offline store's "One" (single-collection) design is incompatible with the universal test suite's DataSourceCreator contract, which does not provide join-key information at data creation time. Remove the AVAILABLE_OFFLINE_STORES registration so universal tests are not parametrized with MongoDB. The 17 dedicated unit tests in test_mongodb.py continue to run and validate the One design. Signed-off-by: Casey Clements --- .secrets.baseline | 6 +++--- .../feature_repos/repo_configuration.py | 19 ------------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 6ade5e5c610..9cf8e2f7342 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1460,14 +1460,14 @@ "filename": "sdk/python/tests/universal/feature_repos/repo_configuration.py", "hashed_secret": "d90e76ef629fb00c95f4e84fec29fbda111e2392", "is_verified": false, - "line_number": 478 + "line_number": 459 }, { "type": "Secret Keyword", "filename": "sdk/python/tests/universal/feature_repos/repo_configuration.py", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 480 + "line_number": 461 } ], "sdk/python/tests/universal/feature_repos/universal/data_sources/file.py": [ @@ -1539,5 +1539,5 @@ } ] }, - "generated_at": "2026-04-23T17:55:15Z" + "generated_at": "2026-04-24T22:04:48Z" } diff --git a/sdk/python/tests/universal/feature_repos/repo_configuration.py b/sdk/python/tests/universal/feature_repos/repo_configuration.py index 252c9a48925..ddd952f71dc 100644 --- a/sdk/python/tests/universal/feature_repos/repo_configuration.py +++ b/sdk/python/tests/universal/feature_repos/repo_configuration.py @@ -108,25 +108,6 @@ ] ) -# MongoDB offline store (requires testcontainers and pymongo) -if os.getenv("FEAST_LOCAL_ONLINE_CONTAINER", "False") == "True": - try: - from tests.universal.feature_repos.universal.data_sources.mongodb import ( - MongoDBDataSourceCreator, - ) - - AVAILABLE_OFFLINE_STORES.extend( - [ - ("local", MongoDBDataSourceCreator), - ] - ) - OFFLINE_STORE_TO_PROVIDER_CONFIG["mongodb"] = ( - "local", - MongoDBDataSourceCreator, - ) - except ImportError: - pass # pymongo or testcontainers not installed - AVAILABLE_ONLINE_STORES: Dict[ str, Tuple[Union[str, Dict[Any, Any]], Optional[Type[OnlineStoreCreator]]] ] = {"sqlite": ({"type": "sqlite"}, None)} From a9b9e8f415b2b9b29e7895b3f68165408767abb4 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Fri, 24 Apr 2026 18:13:23 -0400 Subject: [PATCH 66/76] Regenerate pixi lockfile after pymongo addition to ci extras Signed-off-by: Casey Clements --- pixi.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pixi.lock b/pixi.lock index 193bb7ea383..52df5185603 100644 --- a/pixi.lock +++ b/pixi.lock @@ -2229,8 +2229,8 @@ packages: requires_python: '>=3.10' - pypi: ./ name: feast - version: 0.60.1.dev87+ge33047ba1 - sha256: d7bc70f699b91a11b2fe3f88eb56144eca2156776247d94c40f658ab39caa4a8 + version: 0.62.1.dev99+g2905b80ea + sha256: 3288a218a8fe35396a2be96fc68a282a0834eaf5bf0db5e90ac0a2e72d957e9e requires_dist: - click>=7.0.0,<9.0.0 - colorama>=0.3.9,<1 @@ -2345,7 +2345,7 @@ packages: - minio==7.2.11 ; extra == 'test' - python-keycloak==4.2.2 ; extra == 'test' - cryptography>=43.0 ; extra == 'test' - - feast[aws,azure,cassandra,clickhouse,couchbase,delta,docling,duckdb,elasticsearch,faiss,gcp,ge,go,grpcio,hazelcast,hbase,ibis,image,k8s,mcp,milvus,mssql,mysql,openlineage,opentelemetry,oracle,postgres,pytorch,qdrant,rag,ray,redis,singlestore,snowflake,spark,sqlite-vec,test,trino] ; extra == 'ci' + - feast[aws,azure,cassandra,clickhouse,couchbase,delta,docling,duckdb,elasticsearch,faiss,gcp,ge,go,grpcio,hazelcast,hbase,ibis,image,k8s,mcp,milvus,mongodb,mssql,mysql,openlineage,opentelemetry,oracle,postgres,pytorch,qdrant,rag,ray,redis,singlestore,snowflake,spark,sqlite-vec,test,trino] ; extra == 'ci' - build ; extra == 'ci' - virtualenv==20.23.0 ; extra == 'ci' - dbt-artifacts-parser ; extra == 'ci' From f558aa55e174799eb4754923d1e1a760760acf54 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 28 Apr 2026 18:53:25 -0400 Subject: [PATCH 67/76] Fix scoring_path heuristic: check entity uniqueness per-FV, not globally The scoring_path detection checked entity uniqueness against the union of all feature view join keys. When FVs have different join key sets (e.g. FV_A on user_id, FV_B on (user_id, device_id)), entity_df can be unique on the union but have duplicate entity_ids for FVs with fewer keys. The $group stage would then discard valid older documents, causing silent NULL results. Move the scoring_path decision inside the per-FV loop, checking uniqueness on each FV's serialized entity_id column. Each FV now independently picks the scoring or training path based on its own key cardinality. Add test_mixed_join_key_cardinality covering the exact scenario. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 17 +-- .../mongodb_offline_store/test_mongodb.py | 139 ++++++++++++++++++ 2 files changed, 147 insertions(+), 9 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index f5d1bb9bfd0..2adfa9bcffe 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -633,15 +633,6 @@ def _run_single(entity_subset_df: pd.DataFrame, coll: Any) -> pd.DataFrame: max_ts = result[event_timestamp_col].max() min_ts = result[event_timestamp_col].min() - # Detect scoring vs training path once per chunk. - all_entity_id_cols = list( - {jk for jks in fv_mapped_join_keys.values() for jk in jks} - & set(result.columns) - ) - scoring_path = result[all_entity_id_cols].drop_duplicates().shape[0] == len( - result - ) - # Process each feature view projection independently. # (Different projections of the same FV have different # entity key mappings and must be handled separately.) @@ -693,6 +684,14 @@ def _ser(row, __mk=_mk, __rjk=_rjk, __okt=_okt): result[eid_col] = result.apply(_ser, axis=1) unique_eids = result[eid_col].unique().tolist() + # Detect scoring vs training path per-FV. + # Scoring path ($group) is only safe when entity_ids for + # THIS feature view are unique in the chunk. When FVs have + # different join key sets, entity_df may be unique on the + # union of all keys but have duplicates for a FV with fewer + # keys — using $group in that case would discard valid docs. + scoring_path = result[eid_col].nunique() == len(result) + # TTL filter — use min_ts for the lower bound so that # documents needed for early entity rows are included. # Per-row TTL enforcement happens in the merge_asof path. diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py index 79a306f8e1e..64fc5449402 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py @@ -558,6 +558,145 @@ def test_k_collapse_multiple_feature_views( assert result_df.loc[1, "mileage"] == 120000 +# --------------------------------------------------------------------------- +# Tests: mixed join key cardinality (scoring_path per-FV) +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_mixed_join_key_cardinality( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """FVs with different join key sets must not lose data via $group. + + FV_A uses (user_id), FV_B uses (user_id, device_id). + entity_df has two rows: (user=1, device=X, t=09:00) and (user=1, device=Y, t=10:00). + Unique on (user_id, device_id), but user_id=1 appears twice. + + For FV_A, both rows map to the same entity_id. The old code would use + the scoring path ($group) globally, discarding older docs for user_id=1. + The fix detects that FV_A's entity_id is non-unique and falls back to + merge_asof for FV_A while FV_B can still use the scoring path. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + + now = datetime.now(tz=pytz.UTC) + t_0800 = now - timedelta(hours=2) # 08:00 + t_0900 = now - timedelta(hours=1) # 09:00 + t_0930 = now - timedelta(minutes=30) # 09:30 + + user_entity = Entity( + name="user_id", join_keys=["user_id"], value_type=ValueType.INT64 + ) + device_entity = Entity( + name="device_id", join_keys=["device_id"], value_type=ValueType.STRING + ) + + # FV_A: keyed on user_id only + source_a = MongoDBSource(name="user_prefs") + fv_a = FeatureView( + name="user_prefs", + entities=[user_entity], + schema=[ + Field(name="user_id", dtype=Int64), + Field(name="theme", dtype=String), + ], + source=source_a, + ttl=timedelta(days=1), + ) + + # FV_B: keyed on (user_id, device_id) + source_b = MongoDBSource(name="device_stats") + fv_b = FeatureView( + name="device_stats", + entities=[user_entity, device_entity], + schema=[ + Field(name="user_id", dtype=Int64), + Field(name="device_id", dtype=String), + Field(name="battery", dtype=Float64), + ], + source=source_b, + ttl=timedelta(days=1), + ) + + # Insert docs for FV_A (user_id only key) + # Two docs for user 1: one at 08:00 ("dark") and one at 09:30 ("light") + docs = [ + { + "entity_id": _make_entity_id({"user_id": 1}), + "feature_view": "user_prefs", + "features": {"theme": "dark"}, + "event_timestamp": t_0800, + "created_at": t_0800, + }, + { + "entity_id": _make_entity_id({"user_id": 1}), + "feature_view": "user_prefs", + "features": {"theme": "light"}, + "event_timestamp": t_0930, + "created_at": t_0930, + }, + # FV_B: keyed on (user_id, device_id) + { + "entity_id": _make_entity_id({"device_id": "X", "user_id": 1}), + "feature_view": "device_stats", + "features": {"battery": 0.8}, + "event_timestamp": t_0800, + "created_at": t_0800, + }, + { + "entity_id": _make_entity_id({"device_id": "Y", "user_id": 1}), + "feature_view": "device_stats", + "features": {"battery": 0.6}, + "event_timestamp": t_0800, + "created_at": t_0800, + }, + ] + collection.insert_many(docs) + client.close() + + # entity_df: two rows, unique on (user_id, device_id) but NOT on user_id + entity_df = pd.DataFrame( + { + "user_id": [1, 1], + "device_id": ["X", "Y"], + "event_timestamp": [t_0900, now], # 09:00 and 10:00 + } + ) + + job = MongoDBOfflineStore.get_historical_features( + config=repo_config, + feature_views=[fv_a, fv_b], + feature_refs=["user_prefs:theme", "device_stats:battery"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + strict_pit=True, + ) + + result_df = job.to_df().sort_values("device_id").reset_index(drop=True) + + assert len(result_df) == 2 + + # Row 0: device=X, request at 09:00 + # FV_A (user_id=1): 09:30 doc is AFTER 09:00 (strict_pit), 08:00 doc is valid → "dark" + # FV_B (user=1, device=X): 08:00 doc is before 09:00 → battery=0.8 + assert result_df.loc[0, "theme"] == "dark", ( + f"Expected 'dark' for row at 09:00 but got {result_df.loc[0, 'theme']!r}. " + "The scoring path $group may have discarded the 08:00 doc." + ) + assert result_df.loc[0, "battery"] == pytest.approx(0.8) + + # Row 1: device=Y, request at 10:00 + # FV_A (user_id=1): 09:30 doc is before 10:00 → "light" + # FV_B (user=1, device=Y): 08:00 doc is before 10:00 → battery=0.6 + assert result_df.loc[1, "theme"] == "light" + assert result_df.loc[1, "battery"] == pytest.approx(0.6) + + # --------------------------------------------------------------------------- # Tests: overlapping feature names # --------------------------------------------------------------------------- From 349c5f16b7d4a433f9ed9eff131994abf6e34ae8 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 28 Apr 2026 18:55:16 -0400 Subject: [PATCH 68/76] Fix offline_write_batch: use original join key names for entity serialization offline_write_batch used mapped (aliased) join key names when serializing entity keys, while get_historical_features uses original names. When a join_key_map is configured, the entity_id bytes would differ between write and read, causing all features to silently return NULL. Use ec.name (original) instead of the mapped name so bytes match. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 2adfa9bcffe..afb9cfbb4af 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -935,11 +935,11 @@ def offline_write_batch( db_name = config.offline_store.database collection_name = config.offline_store.collection + # Use original (unmapped) join key names so that the serialized + # entity_id bytes match those produced by get_historical_features, + # which also serializes with original names (see _ser at line ~662). join_key_types: Dict[str, ValueType] = { - feature_view.projection.join_key_map.get( - ec.name, ec.name - ): ec.dtype.to_value_type() - for ec in feature_view.entity_columns + ec.name: ec.dtype.to_value_type() for ec in feature_view.entity_columns } join_keys = list(join_key_types.keys()) From 8f8de115ce8efe4e7b79c4c57663683e627ddab9 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Tue, 28 Apr 2026 19:09:23 -0400 Subject: [PATCH 69/76] Fix pull_latest and pull_all to return join key columns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pull_latest_from_table_or_query and pull_all_from_table_or_query only returned serialized entity_id bytes, event_timestamp, and feature columns — but not the actual join key columns (e.g. driver_id) that the base class contract requires. Add _expand_entity_id_column helper that deserializes entity_id bytes back into individual join key columns using deserialize_entity_key. Both methods now call this helper before returning the DataFrame. Add assertions in existing pull_latest and pull_all tests to verify join key columns are present in the output. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 39 ++++++++++++++++++- .../mongodb_offline_store/test_mongodb.py | 10 +++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index afb9cfbb4af..7fcd2b8aa17 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -69,7 +69,7 @@ SavedDatasetLocationAlreadyExists, ) from feast.feature_view import FeatureView -from feast.infra.key_encoding_utils import serialize_entity_key +from feast.infra.key_encoding_utils import deserialize_entity_key, serialize_entity_key from feast.infra.offline_stores.offline_store import ( OfflineStore, RetrievalJob, @@ -337,6 +337,38 @@ def _serialize_entity_key_from_row( return serialize_entity_key(entity_key, entity_key_version) +def _expand_entity_id_column( + df: pd.DataFrame, + join_key_columns: List[str], + entity_key_version: int, +) -> pd.DataFrame: + """Deserialize ``entity_id`` bytes into individual join key columns. + + Each ``entity_id`` value is deserialized via ``deserialize_entity_key`` + and the resulting join key names/values are added as new columns. + The ``entity_id`` column is then dropped. + """ + if "entity_id" not in df.columns or df.empty: + return df + + def _extract(eid_bytes: bytes) -> Dict[str, Any]: + ek = deserialize_entity_key(eid_bytes, entity_key_version) + row: Dict[str, Any] = {} + for key, val in zip(ek.join_keys, ek.entity_values): + which = val.WhichOneof("val") + row[key] = getattr(val, which) if which else None + return row + + expanded = pd.DataFrame([_extract(eid) for eid in df["entity_id"]]) + # Only keep the columns the caller asked for (in case the serialized + # key contains more keys than expected). + for col in join_key_columns: + if col in expanded.columns: + df[col] = expanded[col].values + df = df.drop(columns=["entity_id"], errors="ignore") + return df + + # --------------------------------------------------------------------------- # Offline store # --------------------------------------------------------------------------- @@ -428,6 +460,8 @@ def pull_latest_from_table_or_query( {"$project": project_stage}, ] + entity_key_version = config.entity_key_serialization_version + def _run() -> pyarrow.Table: client = MongoDBOfflineStore._get_client_and_ensure_indexes(config) try: @@ -435,6 +469,7 @@ def _run() -> pyarrow.Table: if not docs: return pyarrow.Table.from_pydict({}) df = pd.DataFrame(docs) + df = _expand_entity_id_column(df, join_key_columns, entity_key_version) if not df.empty and "event_timestamp" in df.columns: if df["event_timestamp"].dt.tz is None: df["event_timestamp"] = pd.to_datetime( @@ -485,6 +520,7 @@ def pull_all_from_table_or_query( for feat in feature_name_columns: project_stage[feat] = f"$features.{feat}" pipeline = [{"$match": match_filter}, {"$project": project_stage}] + entity_key_version = config.entity_key_serialization_version def _run() -> pyarrow.Table: client = MongoDBOfflineStore._get_client_and_ensure_indexes(config) @@ -493,6 +529,7 @@ def _run() -> pyarrow.Table: if not docs: return pyarrow.Table.from_pydict({}) df = pd.DataFrame(docs) + df = _expand_entity_id_column(df, join_key_columns, entity_key_version) if not df.empty and "event_timestamp" in df.columns: if df["event_timestamp"].dt.tz is None: df["event_timestamp"] = pd.to_datetime( diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py index 64fc5449402..05f097d87eb 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py @@ -250,6 +250,11 @@ def test_pull_latest_from_table_or_query( assert isinstance(df, pd.DataFrame) assert len(df) == 2 # Two unique entities + # Join key columns must be present in the output. + assert "driver_id" in df.columns, ( + f"Expected 'driver_id' in output columns, got {list(df.columns)}" + ) + conv_rates = sorted(df["conv_rate"].tolist()) assert conv_rates[0] == pytest.approx(0.3) # Driver 2's only value assert conv_rates[1] == pytest.approx(0.7) # Driver 1's latest @@ -374,6 +379,11 @@ def test_pull_all_from_table_or_query( # Excludes: driver 1 at 2h ago (before start_date) and driver 2 at 2h ago. assert len(df) == 2 + # Join key columns must be present in the output. + assert "driver_id" in df.columns, ( + f"Expected 'driver_id' in output columns, got {list(df.columns)}" + ) + # --------------------------------------------------------------------------- # Tests: TTL From 9de55482156a37d7775c1ecd16aef933da08821b Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 29 Apr 2026 09:52:55 -0400 Subject: [PATCH 70/76] Fix scoring_path: require homogeneous timestamps to prevent data loss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scoring path ($group $first) selects the latest doc per entity up to max_ts — the maximum request timestamp across ALL entities in the chunk. When entities have different request timestamps, $group may pick a doc that post-dates a specific entity's request time. The Python future_mask would null it, but the valid older doc was already discarded by $group. Add a second condition: scoring_path is only used when all entity request timestamps in the chunk are identical, which is the common real-time scoring case (all requests at 'now'). When timestamps differ, fall back to the training path (merge_asof) which handles per-row PIT correctness. Add test_heterogeneous_timestamps_fall_back_to_training_path. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 28 ++++- .../mongodb_offline_store/test_mongodb.py | 106 ++++++++++++++++++ 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 7fcd2b8aa17..f38adae4fc8 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -722,12 +722,28 @@ def _ser(row, __mk=_mk, __rjk=_rjk, __okt=_okt): unique_eids = result[eid_col].unique().tolist() # Detect scoring vs training path per-FV. - # Scoring path ($group) is only safe when entity_ids for - # THIS feature view are unique in the chunk. When FVs have - # different join key sets, entity_df may be unique on the - # union of all keys but have duplicates for a FV with fewer - # keys — using $group in that case would discard valid docs. - scoring_path = result[eid_col].nunique() == len(result) + # + # The scoring path ($group $first) requires unique entity + # IDs for THIS FV — otherwise $group discards rows that + # share an entity_id. + # + # When strict_pit=True, there is an additional constraint: + # all entity request timestamps must be identical. The + # $match uses $lte: max_ts which is correct only when + # every entity shares the same request time. When + # timestamps differ, $group may pick a doc that is after + # a specific entity's request time; the Python future_mask + # would null it, but the valid older doc was already + # discarded by $group. + # + # When strict_pit=False, there is no $lte filter and no + # future_mask — we want the globally latest doc per entity + # regardless of request time, so $group $first is always + # correct. + unique_entities = result[eid_col].nunique() == len(result) + scoring_path = unique_entities and ( + not strict_pit or result[event_timestamp_col].nunique() == 1 + ) # TTL filter — use min_ts for the lower bound so that # documents needed for early entity rows are included. diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py index 05f097d87eb..501addb8dc2 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py @@ -707,6 +707,112 @@ def test_mixed_join_key_cardinality( assert result_df.loc[1, "battery"] == pytest.approx(0.6) +# --------------------------------------------------------------------------- +# Tests: heterogeneous timestamps prevent scoring path +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_heterogeneous_timestamps_fall_back_to_training_path( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Unique entity IDs but different request timestamps must use training path. + + Entity A requests at 09:00, Entity B at 10:00. + Doc for A: 08:00 (valid) and 09:30 (future for A's request). + Doc for B: 09:00 (valid). + + If the scoring path were used, $group with $lte max_ts=10:00 would pick + the 09:30 doc for A. future_mask would null it, but the valid 08:00 doc + was already discarded. The training path (merge_asof) correctly returns + the 08:00 doc for A. + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + + now = datetime.now(tz=pytz.UTC) + t_0800 = now - timedelta(hours=2) + t_0900 = now - timedelta(hours=1) + t_0930 = now - timedelta(minutes=30) + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + source = MongoDBSource(name="driver_stats_het") + fv = FeatureView( + name="driver_stats_het", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + + docs = [ + # Driver 1: valid doc at 08:00 + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_het", + "features": {"conv_rate": 0.5}, + "event_timestamp": t_0800, + "created_at": t_0800, + }, + # Driver 1: doc at 09:30 (future relative to driver 1's request at 09:00) + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_het", + "features": {"conv_rate": 0.9}, + "event_timestamp": t_0930, + "created_at": t_0930, + }, + # Driver 2: valid doc at 09:00 + { + "entity_id": _make_entity_id({"driver_id": 2}), + "feature_view": "driver_stats_het", + "features": {"conv_rate": 0.7}, + "event_timestamp": t_0900, + "created_at": t_0900, + }, + ] + collection.insert_many(docs) + client.close() + + # Different request timestamps: driver 1 at 09:00, driver 2 at 10:00 + entity_df = pd.DataFrame( + { + "driver_id": [1, 2], + "event_timestamp": [t_0900, now], + } + ) + + job = MongoDBOfflineStore.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["driver_stats_het:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + strict_pit=True, + ) + + result_df = job.to_df().sort_values("driver_id").reset_index(drop=True) + assert len(result_df) == 2 + + # Driver 1: request at 09:00, 09:30 doc is future → must return 08:00 doc (0.5) + assert result_df.loc[0, "conv_rate"] == pytest.approx(0.5), ( + f"Expected 0.5 for driver 1 (08:00 doc) but got " + f"{result_df.loc[0, 'conv_rate']!r}. The scoring path may have " + f"discarded the valid older doc via $group." + ) + + # Driver 2: request at 10:00, 09:00 doc is valid → 0.7 + assert result_df.loc[1, "conv_rate"] == pytest.approx(0.7) + + # --------------------------------------------------------------------------- # Tests: overlapping feature names # --------------------------------------------------------------------------- From c34b6cdcd06baa5f41c0f9db690b275496db967e Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 29 Apr 2026 10:25:56 -0400 Subject: [PATCH 71/76] Fix training path: sort fv_df by created_at to break event_timestamp ties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The training path (merge_asof) sorted fv_df only by event_timestamp. When multiple docs for the same entity share the same event_timestamp but differ in created_at, merge_asof picks the last row in sorted order. Without a created_at sort, that order depends on MongoDB's undefined document return order — making the result non-deterministic. Sort by [event_timestamp, created_at] so merge_asof consistently picks the doc with the highest created_at among ties, matching the scoring path's behavior ($sort + $group $first). Add test_training_path_created_at_tiebreaker. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 4 +- .../mongodb_offline_store/test_mongodb.py | 91 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index f38adae4fc8..079e61aa4fd 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -863,7 +863,9 @@ def _ser(row, __mk=_mk, __rjk=_rjk, __okt=_okt): result = result.sort_values(event_timestamp_col).reset_index( drop=True ) - fv_df = fv_df.sort_values("event_timestamp").reset_index(drop=True) + fv_df = fv_df.sort_values( + ["event_timestamp", "created_at"] + ).reset_index(drop=True) merge_cols = [eid_col, "event_timestamp"] + [ f for f in features if f in fv_df.columns ] diff --git a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py index 501addb8dc2..8fabcf7b28a 100644 --- a/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py +++ b/sdk/python/tests/unit/infra/offline_stores/contrib/mongodb_offline_store/test_mongodb.py @@ -309,6 +309,97 @@ def test_get_historical_features_training_path( assert result_df.loc[2, "conv_rate"] == pytest.approx(0.3) +# --------------------------------------------------------------------------- +# Tests: training path created_at tiebreaker +# --------------------------------------------------------------------------- + + +@_requires_docker +def test_training_path_created_at_tiebreaker( + repo_config: RepoConfig, mongodb_connection_string: str +) -> None: + """Training path must pick the highest created_at when event_timestamps tie. + + Two docs for the same entity with identical event_timestamp but different + created_at values. Without sorting fv_df on created_at, merge_asof would + pick whichever doc MongoDB returned first (undefined order). + """ + client: MongoClient = MongoClient(mongodb_connection_string) + db = client["feast_test"] + collection = db["feature_history"] + collection.drop() + + now = datetime.now(tz=pytz.UTC) + t_feature = now - timedelta(hours=1) + created_early = now - timedelta(minutes=30) + created_late = now - timedelta(minutes=10) + + driver_entity = Entity( + name="driver_id", join_keys=["driver_id"], value_type=ValueType.INT64 + ) + source = MongoDBSource(name="driver_stats_tiebreak") + fv = FeatureView( + name="driver_stats_tiebreak", + entities=[driver_entity], + schema=[ + Field(name="driver_id", dtype=Int64), + Field(name="conv_rate", dtype=Float64), + ], + source=source, + ttl=timedelta(days=1), + ) + + docs = [ + # Same entity, same event_timestamp, earlier created_at → stale + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_tiebreak", + "features": {"conv_rate": 0.1}, + "event_timestamp": t_feature, + "created_at": created_early, + }, + # Same entity, same event_timestamp, later created_at → should win + { + "entity_id": _make_entity_id({"driver_id": 1}), + "feature_view": "driver_stats_tiebreak", + "features": {"conv_rate": 0.9}, + "event_timestamp": t_feature, + "created_at": created_late, + }, + ] + collection.insert_many(docs) + client.close() + + # Repeated entity → forces training path (merge_asof) + entity_df = pd.DataFrame( + { + "driver_id": [1, 1], + "event_timestamp": [now, now], + } + ) + + job = MongoDBOfflineStore.get_historical_features( + config=repo_config, + feature_views=[fv], + feature_refs=["driver_stats_tiebreak:conv_rate"], + entity_df=entity_df, + registry=MagicMock(), + project=repo_config.project, + full_feature_names=False, + ) + + result_df = job.to_df() + assert len(result_df) == 2 + + # Both rows should get conv_rate=0.9 (the doc with later created_at) + for idx in range(len(result_df)): + assert result_df.loc[idx, "conv_rate"] == pytest.approx(0.9), ( + f"Row {idx}: expected 0.9 (latest created_at) but got " + f"{result_df.loc[idx, 'conv_rate']!r}. " + f"Training path may not be sorting by created_at." + ) + + # --------------------------------------------------------------------------- # Tests: get_historical_features (scoring path — unique entity IDs) # --------------------------------------------------------------------------- From 1d2a1c937c8b07e4802f4c1d80c1402a833c0050 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 29 Apr 2026 13:41:36 -0400 Subject: [PATCH 72/76] Clean up stale docstrings: remove references to MongoDBOfflineStoreOne/Agg Remove leftover references to earlier naming iterations (MongoDBSourceOne, 'agg offline store', 'Improves on MongoDBOfflineStoreOne') from class and method docstrings. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 079e61aa4fd..46883a99c5d 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -102,7 +102,7 @@ class MongoDBOfflineStoreConfig(FeastConfigBaseModel): - """Configuration for the MongoDB agg offline store (single shared collection).""" + """Configuration for the MongoDB offline store (single shared collection).""" type: StrictStr = "feast.infra.offline_stores.contrib.mongodb_offline_store.mongodb.MongoDBOfflineStore" @@ -122,10 +122,10 @@ class MongoDBOfflineStoreConfig(FeastConfigBaseModel): class MongoDBSource(DataSource): - """Data source for the aggregation offline store. + """Data source for the MongoDB offline store. - Identical semantics to MongoDBSourceOne: the ``name`` field is used as - the ``feature_view`` discriminator inside the single shared collection. + The ``name`` field is used as the ``feature_view`` discriminator inside + the single shared collection. """ def __init__( @@ -377,7 +377,7 @@ def _extract(eid_bytes: bytes) -> Dict[str, Any]: class MongoDBOfflineStore(OfflineStore): """MongoDB offline store using a single collection and grouped aggregation. - Improves on MongoDBOfflineStoreOne by: + Key optimizations: - Collapsing K feature-view queries into one aggregation per join-key group - Using server-side ``$group`` (O(log P) with index) for the scoring path """ From 02e457c082081ff66fcc65f9fe51efbdae4098fd Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Wed, 29 Apr 2026 13:41:36 -0400 Subject: [PATCH 73/76] Clean up stale docstrings: remove references to MongoDBOfflineStoreOne/Agg Remove leftover references to earlier naming iterations (MongoDBSourceOne, 'agg offline store', 'Improves on MongoDBOfflineStoreOne') from class and method docstrings. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 46883a99c5d..037c426b9da 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -15,8 +15,7 @@ """ MongoDB Offline Store. -Single-collection schema identical to mongodb_one. The query core differs -in two ways: +Single-collection schema. Key optimizations: 1. K-collapse: feature views that share the same join key set are batched into a single ``$match + $sort`` aggregation instead of K separate find @@ -300,7 +299,7 @@ def persist( # --------------------------------------------------------------------------- -# Helpers (copied from mongodb_one.py) +# Helpers # --------------------------------------------------------------------------- @@ -567,7 +566,7 @@ def get_historical_features( Training path (repeated entity IDs at different timestamps): Omits ``$group`` and uses ``merge_asof`` in Python, matching - mongodb_one behaviour but still with K-collapsed queries. + standard PIT behaviour but still with K-collapsed queries. Args: strict_pit: When True (default) features whose document timestamp From ae5256fd145326a533e98d1f24af1a8f9f18b5d5 Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 30 Apr 2026 09:15:41 -0400 Subject: [PATCH 74/76] Added driver metadata to clients Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 037c426b9da..5b422a78cae 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -87,6 +87,8 @@ from feast.type_map import mongodb_to_feast_value_type from feast.value_type import ValueType +from . import DRIVER_METADATA + # Cache: avoid re-creating the compound index on every call _indexes_ensured: Set[str] = set() @@ -199,7 +201,9 @@ def get_table_column_names_and_types( """Infer column names and types by reading a sample document from MongoDB.""" if MongoClient is None: raise FeastExtrasDependencyImportError("pymongo", "mongodb") - client: Any = MongoClient(config.offline_store.connection_string) + client: Any = MongoClient( + config.offline_store.connection_string, driver=DRIVER_METADATA + ) try: coll = client[config.offline_store.database][ config.offline_store.collection @@ -405,7 +409,7 @@ def _get_client_and_ensure_indexes(config: RepoConfig) -> Any: db_name = config.offline_store.database collection = config.offline_store.collection cache_key = f"{conn_str}/{db_name}/{collection}" - client: Any = MongoClient(conn_str) + client: Any = MongoClient(conn_str, driver=DRIVER_METADATA) if cache_key not in _indexes_ensured: MongoDBOfflineStore._ensure_indexes(client, db_name, collection) _indexes_ensured.add(cache_key) From f1996fc2f654d84bd94367279cb3f550c4b4d24e Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 30 Apr 2026 09:56:51 -0400 Subject: [PATCH 75/76] Update .secrets.baseline Signed-off-by: Casey Clements --- .secrets.baseline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.secrets.baseline b/.secrets.baseline index 9cf8e2f7342..c0a235ad480 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1539,5 +1539,5 @@ } ] }, - "generated_at": "2026-04-24T22:04:48Z" + "generated_at": "2026-04-30T13:56:37Z" } From b3b7563fdafbc1dfa355b7c515f9a5fcdf9db37f Mon Sep 17 00:00:00 2001 From: Casey Clements Date: Thu, 30 Apr 2026 11:11:13 -0400 Subject: [PATCH 76/76] Remove preview warnings from MongoDB offline store The offline store is GA. Remove the three RuntimeWarning calls in pull_latest_from_table_or_query, pull_all_from_table_or_query, and get_historical_features, along with the unused warnings import. Signed-off-by: Casey Clements --- .../contrib/mongodb_offline_store/mongodb.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py index 5b422a78cae..07e35f66c15 100644 --- a/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py +++ b/sdk/python/feast/infra/offline_stores/contrib/mongodb_offline_store/mongodb.py @@ -36,7 +36,6 @@ (entity_id ASC, feature_view ASC, event_timestamp DESC, created_at DESC) """ -import warnings from collections import defaultdict from datetime import datetime, timezone from typing import ( @@ -431,10 +430,6 @@ def pull_latest_from_table_or_query( f"MongoDBOfflineStore expected MongoDBSource, " f"got {type(data_source).__name__!r}." ) - warnings.warn( - "MongoDB offline store is in preview. API may change without notice.", - RuntimeWarning, - ) db_name = config.offline_store.database collection = config.offline_store.collection feature_view_name = data_source.feature_view_name @@ -502,10 +497,6 @@ def pull_all_from_table_or_query( f"MongoDBOfflineStore expected MongoDBSource, " f"got {type(data_source).__name__!r}." ) - warnings.warn( - "MongoDB offline store is in preview. API may change without notice.", - RuntimeWarning, - ) db_name = config.offline_store.database collection = config.offline_store.collection feature_view_name = data_source.feature_view_name @@ -583,10 +574,6 @@ def get_historical_features( raise ValueError( "MongoDBOfflineStore does not support SQL entity_df strings." ) - warnings.warn( - "MongoDB offline store is in preview. API may change without notice.", - RuntimeWarning, - ) db_name = config.offline_store.database feature_collection = config.offline_store.collection