FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
feast/sdk/python/feast/feature_service.py at master · feast-dev/feast · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
feast-dev
/
feast
Public
Notifications
You must be signed in to change notification settings
Fork
1.4k
Star
7.2k
Code
Issues
218
Pull requests
191
Discussions
Actions
Security and quality
1
Insights
Additional navigation options
Code
Issues
Pull requests
Discussions
Actions
Security and quality
Insights
Expand file tree
Breadcrumbs
feast
/
sdk
/
python
/
feast
/
feature_service.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
433 lines (380 loc) · 16.8 KB
Breadcrumbs
feast
/
sdk
/
python
/
feast
/
feature_service.py
Copy path
File metadata and controls
433 lines (380 loc) · 16.8 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
from
datetime
import
datetime
from
typing
import
TYPE_CHECKING
,
Dict
,
List
,
Optional
,
Union
from
google
.
protobuf
.
json_format
import
MessageToJson
from
typeguard
import
typechecked
from
feast
.
base_feature_view
import
BaseFeatureView
from
feast
.
errors
import
(
FeastObjectNotFoundException
,
FeatureViewMissingDuringFeatureServiceInference
,
)
from
feast
.
feature_logging
import
LoggingConfig
from
feast
.
feature_view
import
FeatureView
from
feast
.
feature_view_projection
import
FeatureViewProjection
from
feast
.
field
import
Field
from
feast
.
labeling
.
label_view
import
LabelView
from
feast
.
on_demand_feature_view
import
OnDemandFeatureView
from
feast
.
protos
.
feast
.
core
.
FeatureService_pb2
import
(
FeatureService
as
FeatureServiceProto
,
)
from
feast
.
protos
.
feast
.
core
.
FeatureService_pb2
import
(
FeatureServiceMeta
as
FeatureServiceMetaProto
,
)
from
feast
.
protos
.
feast
.
core
.
FeatureService_pb2
import
(
FeatureServiceSpec
as
FeatureServiceSpecProto
,
)
if
TYPE_CHECKING
:
from
feast
.
infra
.
registry
.
base_registry
import
BaseRegistry
@
typechecked
class
FeatureService
:
"""
A feature service defines a logical group of features from one or more feature views.
This group of features can be retrieved together during training or serving.
Attributes:
name: The unique name of the feature service.
feature_view_projections: A list containing feature views and feature view
projections, representing the features in the feature service.
description: A human-readable description.
tags: A dictionary of key-value pairs to store arbitrary metadata.
owner: The owner of the feature service, typically the email of the primary
maintainer.
created_timestamp: The time when the feature service was created.
last_updated_timestamp: The time when the feature service was last updated.
"""
name
:
str
_features
:
List
[
Union
[
FeatureView
,
OnDemandFeatureView
,
LabelView
]]
feature_view_projections
:
List
[
FeatureViewProjection
]
description
:
str
tags
:
Dict
[
str
,
str
]
owner
:
str
created_timestamp
:
Optional
[
datetime
]
=
None
last_updated_timestamp
:
Optional
[
datetime
]
=
None
logging_config
:
Optional
[
LoggingConfig
]
=
None
precompute_online
:
bool
=
False
def
__init__
(
self
,
*
,
name
:
str
,
features
:
List
[
Union
[
FeatureView
,
OnDemandFeatureView
,
LabelView
]],
tags
:
Optional
[
Dict
[
str
,
str
]]
=
None
,
description
:
str
=
""
,
owner
:
str
=
""
,
logging_config
:
Optional
[
LoggingConfig
]
=
None
,
precompute_online
:
bool
=
False
,
):
"""
Creates a FeatureService object.
Args:
name: The unique name of the feature service.
features: A list containing feature views and feature view
projections, representing the features in the feature service.
description (optional): A human-readable description.
tags (optional): A dictionary of key-value pairs to store arbitrary metadata.
owner (optional): The owner of the feature view, typically the email of the
primary maintainer.
precompute_online (optional): When True, a pre-computed feature vector is
maintained per entity for single-read online retrieval.
"""
self
.
name
=
name
self
.
_features
=
features
self
.
feature_view_projections
=
[]
self
.
description
=
description
self
.
tags
=
tags
or
{}
self
.
owner
=
owner
self
.
created_timestamp
=
None
self
.
last_updated_timestamp
=
None
self
.
logging_config
=
logging_config
self
.
precompute_online
=
precompute_online
for
feature_grouping
in
self
.
_features
:
if
isinstance
(
feature_grouping
,
BaseFeatureView
):
self
.
feature_view_projections
.
append
(
feature_grouping
.
projection
)
def
infer_features
(
self
,
fvs_to_update
:
Dict
[
str
,
Union
[
FeatureView
,
BaseFeatureView
]]
):
"""
Infers the features for the projections of this feature service, and updates this feature
service in place.
This method is necessary since feature services may rely on feature views which require
feature inference.
Args:
fvs_to_update: A mapping of feature view names to corresponding feature views that
contains all the feature views necessary to run inference.
"""
for
feature_grouping
in
self
.
_features
:
if
isinstance
(
feature_grouping
,
BaseFeatureView
):
projection
=
feature_grouping
.
projection
if
projection
.
desired_features
:
# The projection wants to select a specific set of inferred features.
# Example: FeatureService(features=[fv[["inferred_feature"]]]), where
# 'fv' is a feature view that was defined without a schema.
if
feature_grouping
.
name
in
fvs_to_update
:
# First we validate that the selected features have actually been inferred.
desired_features
=
set
(
projection
.
desired_features
)
actual_features
=
set
(
[
f
.
name
for
f
in
fvs_to_update
[
feature_grouping
.
name
].
features
]
)
assert
desired_features
.
issubset
(
actual_features
)
# Then we extract the selected features and add them to the projection.
projection
.
features
=
[]
for
f
in
fvs_to_update
[
feature_grouping
.
name
].
features
:
if
f
.
name
in
desired_features
:
projection
.
features
.
append
(
f
)
else
:
raise
FeatureViewMissingDuringFeatureServiceInference
(
feature_view_name
=
feature_grouping
.
name
,
feature_service_name
=
self
.
name
,
)
continue
if
projection
.
features
:
# The projection has already selected features from a feature view with a
# known schema, so no action needs to be taken.
# Example: FeatureService(features=[fv[["existing_feature"]]]), where
# 'existing_feature' was defined as part of the schema of 'fv'.
# Example: FeatureService(features=[fv]), where 'fv' was defined with a schema.
continue
# The projection wants to select all possible inferred features.
# Example: FeatureService(features=[fv]), where 'fv' is a feature view that
# was defined without a schema.
if
feature_grouping
.
name
in
fvs_to_update
:
projection
.
features
=
fvs_to_update
[
feature_grouping
.
name
].
features
else
:
raise
FeatureViewMissingDuringFeatureServiceInference
(
feature_view_name
=
feature_grouping
.
name
,
feature_service_name
=
self
.
name
,
)
else
:
raise
ValueError
(
f"The feature service
{
self
.
name
}
has been provided with an invalid type "
f'
{
type
(
feature_grouping
)
}
as part of the "features" argument.)'
)
def
prepare_for_apply
(
self
,
registry
:
"BaseRegistry"
,
project
:
str
,
allow_cache
:
bool
=
False
,
)
->
"FeatureService"
:
"""
Materialize feature view projections before registry apply.
Uses the same FeatureService construction and ``infer_features`` path as
``FeatureStore.apply`` for SDK-defined services.
When the service is already fully resolved (SDK path where _features is
set and projections already have features populated via infer_features,
OR the proto deserialization path where projections carry full dtype
info), this is a no-op.
"""
from
feast
.
types
import
Invalid
if
self
.
_features
and
all
(
p
.
features
for
p
in
self
.
feature_view_projections
):
return
self
if
(
not
self
.
_features
and
self
.
feature_view_projections
and
all
(
p
.
features
and
all
(
f
.
dtype
!=
Invalid
for
f
in
p
.
features
)
for
p
in
self
.
feature_view_projections
)
):
return
self
fvs_to_update
:
Dict
[
str
,
Union
[
FeatureView
,
BaseFeatureView
]]
=
{}
if
self
.
_features
:
for
feature_grouping
in
self
.
_features
:
if
isinstance
(
feature_grouping
,
BaseFeatureView
):
fvs_to_update
[
feature_grouping
.
name
]
=
(
registry
.
get_any_feature_view
(
feature_grouping
.
name
,
project
,
allow_cache
=
allow_cache
)
)
self
.
infer_features
(
fvs_to_update
=
fvs_to_update
)
return
self
resolved_features
:
List
[
Union
[
FeatureView
,
OnDemandFeatureView
,
LabelView
]]
=
[]
for
projection
in
self
.
feature_view_projections
:
try
:
feature_view
=
registry
.
get_any_feature_view
(
projection
.
name
,
project
,
allow_cache
=
allow_cache
)
except
FeastObjectNotFoundException
as
exc
:
raise
FeastObjectNotFoundException
(
f"Feature view '
{
projection
.
name
}
' not found in project '
{
project
}
'"
)
from
exc
if
not
isinstance
(
feature_view
, (
FeatureView
,
OnDemandFeatureView
,
LabelView
)
):
raise
ValueError
(
f"Cannot resolve projection for feature view '
{
projection
.
name
}
'"
)
fvs_to_update
[
feature_view
.
name
]
=
feature_view
features_by_name
=
{
feature
.
name
:
feature
for
feature
in
feature_view
.
features
}
if
self
.
_projection_matches_registry_features
(
projection
,
features_by_name
):
resolved_features
.
append
(
feature_view
.
with_projection
(
projection
))
elif
projection
.
desired_features
:
resolved_features
.
append
(
feature_view
[
list
(
projection
.
desired_features
)]
)
elif
not
projection
.
features
:
resolved_features
.
append
(
feature_view
)
else
:
resolved_features
.
append
(
feature_view
[[
feature
.
name
for
feature
in
projection
.
features
]]
)
prepared
=
FeatureService
(
name
=
self
.
name
,
features
=
resolved_features
,
tags
=
self
.
tags
,
description
=
self
.
description
,
owner
=
self
.
owner
,
logging_config
=
self
.
logging_config
,
precompute_online
=
self
.
precompute_online
,
)
prepared
.
created_timestamp
=
self
.
created_timestamp
prepared
.
last_updated_timestamp
=
self
.
last_updated_timestamp
prepared
.
infer_features
(
fvs_to_update
=
fvs_to_update
)
self
.
_features
=
prepared
.
_features
self
.
feature_view_projections
=
prepared
.
feature_view_projections
return
self
@
staticmethod
def
_projection_matches_registry_features
(
projection
:
FeatureViewProjection
,
features_by_name
:
Dict
[
str
,
Field
],
)
->
bool
:
if
not
projection
.
features
:
return
False
for
feature
in
projection
.
features
:
if
feature
.
name
not
in
features_by_name
:
return
False
if
feature
!=
features_by_name
[
feature
.
name
]:
return
False
return
True
def
__repr__
(
self
):
items
=
(
f"
{
k
}
=
{
v
}
"
for
k
,
v
in
self
.
__dict__
.
items
())
return
f"<
{
self
.
__class__
.
__name__
}
(
{
', '
.
join
(
items
)
}
)>"
def
__str__
(
self
):
return
str
(
MessageToJson
(
self
.
to_proto
()))
def
__hash__
(
self
):
return
hash
(
self
.
name
)
def
__eq__
(
self
,
other
):
if
not
isinstance
(
other
,
FeatureService
):
return
False
if
(
self
.
name
!=
other
.
name
or
self
.
description
!=
other
.
description
or
self
.
tags
!=
other
.
tags
or
self
.
owner
!=
other
.
owner
or
self
.
precompute_online
!=
other
.
precompute_online
):
return
False
if
sorted
(
self
.
feature_view_projections
)
!=
sorted
(
other
.
feature_view_projections
):
return
False
return
True
@
classmethod
def
from_proto
(
cls
,
feature_service_proto
:
FeatureServiceProto
):
"""
Converts a FeatureServiceProto to a FeatureService object.
Args:
feature_service_proto: A protobuf representation of a FeatureService.
"""
fs
=
cls
(
name
=
feature_service_proto
.
spec
.
name
,
features
=
[],
tags
=
dict
(
feature_service_proto
.
spec
.
tags
),
description
=
feature_service_proto
.
spec
.
description
,
owner
=
feature_service_proto
.
spec
.
owner
,
logging_config
=
LoggingConfig
.
from_proto
(
feature_service_proto
.
spec
.
logging_config
),
precompute_online
=
feature_service_proto
.
spec
.
precompute_online
,
)
fs
.
feature_view_projections
.
extend
(
[
FeatureViewProjection
.
from_proto
(
projection
)
for
projection
in
feature_service_proto
.
spec
.
features
]
)
if
feature_service_proto
.
meta
.
HasField
(
"created_timestamp"
):
fs
.
created_timestamp
=
(
feature_service_proto
.
meta
.
created_timestamp
.
ToDatetime
()
)
if
feature_service_proto
.
meta
.
HasField
(
"last_updated_timestamp"
):
fs
.
last_updated_timestamp
=
(
feature_service_proto
.
meta
.
last_updated_timestamp
.
ToDatetime
()
)
return
fs
def
to_proto
(
self
)
->
FeatureServiceProto
:
"""
Converts a feature service to its protobuf representation.
Returns:
A FeatureServiceProto protobuf.
"""
meta
=
FeatureServiceMetaProto
()
if
self
.
created_timestamp
:
meta
.
created_timestamp
.
FromDatetime
(
self
.
created_timestamp
)
if
self
.
last_updated_timestamp
:
meta
.
last_updated_timestamp
.
FromDatetime
(
self
.
last_updated_timestamp
)
spec
=
FeatureServiceSpecProto
(
name
=
self
.
name
,
features
=
[
projection
.
to_proto
()
for
projection
in
self
.
feature_view_projections
],
tags
=
self
.
tags
,
description
=
self
.
description
,
owner
=
self
.
owner
,
logging_config
=
self
.
logging_config
.
to_proto
()
if
self
.
logging_config
else
None
,
precompute_online
=
self
.
precompute_online
,
)
return
FeatureServiceProto
(
spec
=
spec
,
meta
=
meta
)
@
classmethod
def
build_apply_request
(
cls
,
*
,
name
:
str
,
project
:
str
,
feature_view_refs
:
List
[
tuple
[
str
,
Optional
[
List
[
str
]]]],
description
:
str
=
""
,
tags
:
Optional
[
Dict
[
str
,
str
]]
=
None
,
owner
:
str
=
""
,
commit
:
bool
=
True
,
):
"""Build an unresolved ApplyFeatureServiceRequest from feature view refs."""
from
feast
.
protos
.
feast
.
core
.
Feature_pb2
import
FeatureSpecV2
from
feast
.
protos
.
feast
.
core
.
FeatureViewProjection_pb2
import
(
FeatureViewProjection
as
FeatureViewProjectionProto
,
)
from
feast
.
protos
.
feast
.
registry
import
RegistryServer_pb2
projections
=
[]
for
feature_view_name
,
feature_names
in
feature_view_refs
:
projection
=
FeatureViewProjectionProto
(
feature_view_name
=
feature_view_name
,
)
if
feature_names
:
for
feature_name
in
feature_names
:
projection
.
feature_columns
.
append
(
FeatureSpecV2
(
name
=
feature_name
))
projections
.
append
(
projection
)
spec
=
FeatureServiceSpecProto
(
name
=
name
,
features
=
projections
,
tags
=
tags
or
{},
description
=
description
,
owner
=
owner
,
)
return
RegistryServer_pb2
.
ApplyFeatureServiceRequest
(
feature_service
=
FeatureServiceProto
(
spec
=
spec
),
project
=
project
,
commit
=
commit
,
)
def
validate
(
self
):
if
not
self
.
precompute_online
:
return
for
fv
in
self
.
_features
:
if
isinstance
(
fv
,
OnDemandFeatureView
)
and
not
fv
.
write_to_online_store
:
raise
ValueError
(
f"FeatureService '
{
self
.
name
}
' has precompute_online=True but "
f"contains OnDemandFeatureView '
{
fv
.
name
}
' with "
f"write_to_online_store=False. On-demand transforms computed at "
f"serve time cannot be pre-computed."
)
Back
|
FazBrowse Home
|
New Git URL