corvic-engine 0.3.0rc43__cp38-abi3-win_amd64.whl → 0.3.0rc45__cp38-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
- import dataclasses
2
1
  import datetime
3
- from collections.abc import Sequence
4
- from typing import Generic, TypeVar, cast
2
+ from collections.abc import Callable, Sequence
3
+ from typing import Any, Protocol, TypeVar, cast
5
4
 
6
5
  import sqlalchemy as sa
7
6
  import sqlalchemy.orm as sa_orm
@@ -9,54 +8,64 @@ from google.protobuf import timestamp_pb2
9
8
  from more_itertools import flatten
10
9
 
11
10
  from corvic import orm
12
- from corvic.result import InvalidArgumentError, Ok
11
+ from corvic.result import InternalError, InvalidArgumentError, Ok
13
12
  from corvic_generated.model.v1alpha import models_pb2
13
+ from corvic_generated.orm.v1 import feature_view_pb2
14
14
 
15
15
  UNCOMMITTED_ID_PREFIX = "__uncommitted_object-"
16
16
 
17
- _Proto = (
18
- models_pb2.Resource
19
- | models_pb2.Source
20
- | models_pb2.FeatureView
21
- | models_pb2.Space
22
- | models_pb2.FeatureViewSource
23
- | models_pb2.Agent
24
- | models_pb2.Pipeline
25
- | models_pb2.Room
26
- | models_pb2.CompletionModel
27
- )
28
- _ID = TypeVar(
29
- "_ID",
30
- orm.ResourceID,
31
- orm.SourceID,
32
- orm.FeatureViewID,
33
- orm.SpaceID,
34
- orm.FeatureViewSourceID,
35
- orm.AgentID,
36
- orm.PipelineID,
37
- orm.RoomID,
38
- orm.CompletionModelID,
17
+ _Proto = TypeVar(
18
+ "_Proto",
19
+ models_pb2.Resource,
20
+ models_pb2.Source,
21
+ models_pb2.FeatureView,
22
+ models_pb2.Space,
23
+ models_pb2.FeatureViewSource,
24
+ models_pb2.Agent,
25
+ models_pb2.Pipeline,
26
+ models_pb2.Room,
27
+ models_pb2.CompletionModel,
39
28
  )
29
+ ID = TypeVar("ID", bound=orm.BaseID[Any])
30
+
31
+
32
+ class _OrmModel(Protocol[ID]):
33
+ id: sa_orm.Mapped[ID | None]
34
+
35
+ org_id: sa_orm.Mapped[orm.OrgID | None]
40
36
 
37
+ @sa.ext.hybrid.hybrid_property
38
+ def created_at(self) -> datetime.datetime | None: ...
41
39
 
42
- @dataclasses.dataclass
43
- class _OrmIDs(Generic[_ID]):
44
- obj_id: _ID | None
45
- room_id: orm.RoomID | None
40
+ @created_at.inplace.expression
41
+ @classmethod
42
+ def _created_at_expression(cls): ...
43
+
44
+
45
+ OrmObj = TypeVar("OrmObj", bound=_OrmModel[Any])
46
+
47
+
48
+ def _translate_orm_id(
49
+ obj_id: str, id_class: type[ID]
50
+ ) -> Ok[ID | None] | orm.InvalidORMIdentifierError:
51
+ if obj_id.startswith(UNCOMMITTED_ID_PREFIX):
52
+ return Ok(None)
53
+ parsed_obj_id = id_class(obj_id)
54
+ match parsed_obj_id.to_db():
55
+ case orm.InvalidORMIdentifierError() as err:
56
+ return err
57
+ case Ok():
58
+ return Ok(parsed_obj_id)
46
59
 
47
60
 
48
61
  def _translate_orm_ids(
49
- proto_obj: _Proto, obj_id_class: type[_ID]
50
- ) -> Ok[_OrmIDs[_ID]] | orm.InvalidORMIdentifierError:
51
- if proto_obj.id.startswith(UNCOMMITTED_ID_PREFIX):
52
- obj_id = None
53
- else:
54
- obj_id = obj_id_class(proto_obj.id)
55
- match obj_id.to_db():
56
- case orm.InvalidORMIdentifierError() as err:
57
- return err
58
- case Ok():
59
- pass
62
+ proto_obj: _Proto, obj_id_class: type[ID]
63
+ ) -> Ok[tuple[ID | None, orm.RoomID | None]] | orm.InvalidORMIdentifierError:
64
+ match _translate_orm_id(proto_obj.id, obj_id_class):
65
+ case orm.InvalidORMIdentifierError() as err:
66
+ return err
67
+ case Ok(obj_id):
68
+ pass
60
69
 
61
70
  match proto_obj:
62
71
  case (
@@ -66,6 +75,7 @@ def _translate_orm_ids(
66
75
  | models_pb2.Space()
67
76
  | models_pb2.Agent()
68
77
  | models_pb2.Pipeline()
78
+ | models_pb2.FeatureViewSource()
69
79
  ):
70
80
  room_id = orm.RoomID(proto_obj.room_id)
71
81
  match room_id.to_db():
@@ -73,12 +83,12 @@ def _translate_orm_ids(
73
83
  return err
74
84
  case Ok():
75
85
  pass
76
- case models_pb2.FeatureViewSource() | models_pb2.CompletionModel():
86
+ case models_pb2.CompletionModel():
77
87
  room_id = None
78
88
  case models_pb2.Room():
79
89
  room_id = cast(orm.RoomID, obj_id)
80
90
 
81
- return Ok(_OrmIDs(obj_id, room_id))
91
+ return Ok((obj_id, room_id))
82
92
 
83
93
 
84
94
  def timestamp_orm_to_proto(
@@ -103,12 +113,11 @@ def resource_orm_to_proto(resource_orm: orm.Resource) -> models_pb2.Resource:
103
113
  size=resource_orm.size,
104
114
  original_path=resource_orm.original_path,
105
115
  room_id=str(resource_orm.room_id),
106
- source_ids=[str(val.source_id) for val in resource_orm.source_associations],
107
116
  org_id=str(resource_orm.org_id),
108
117
  recent_events=[resource_orm.latest_event] if resource_orm.latest_event else [],
109
- pipeline_id=str(resource_orm.pipeline_input_refs[-1].pipeline.id)
110
- if resource_orm.pipeline_input_refs
111
- else None,
118
+ pipeline_id=str(resource_orm.pipeline_ref.pipeline_id)
119
+ if resource_orm.pipeline_ref
120
+ else "",
112
121
  created_at=timestamp_orm_to_proto(resource_orm.created_at),
113
122
  )
114
123
 
@@ -119,13 +128,10 @@ def source_orm_to_proto(source_orm: orm.Source) -> models_pb2.Source:
119
128
  name=source_orm.name,
120
129
  table_op_graph=source_orm.table_op_graph,
121
130
  room_id=str(source_orm.room_id),
122
- resource_id=str(source_orm.resource_associations[0].resource_id)
123
- if source_orm.resource_associations
124
- else "",
125
131
  org_id=str(source_orm.org_id),
126
- pipeline_id=str(source_orm.pipeline_output_refs[-1].pipeline.id)
127
- if source_orm.pipeline_output_refs
128
- else None,
132
+ pipeline_id=str(source_orm.pipeline_ref.pipeline_id)
133
+ if source_orm.pipeline_ref
134
+ else "",
129
135
  created_at=timestamp_orm_to_proto(source_orm.created_at),
130
136
  )
131
137
 
@@ -144,10 +150,17 @@ def agent_orm_to_proto(agent_orm: orm.Agent) -> models_pb2.Agent:
144
150
  def feature_view_source_orm_to_proto(
145
151
  feature_view_source_orm: orm.FeatureViewSource,
146
152
  ) -> models_pb2.FeatureViewSource:
153
+ if feature_view_source_orm.table_op_graph.WhichOneof("op") is not None:
154
+ op = feature_view_source_orm.table_op_graph
155
+ else:
156
+ # some legacy feature views were stored without op graphs fill those
157
+ # with the source's opgraph
158
+ op = feature_view_source_orm.source.table_op_graph
147
159
  return models_pb2.FeatureViewSource(
148
160
  id=str(feature_view_source_orm.id),
161
+ room_id=str(feature_view_source_orm.room_id),
149
162
  source=source_orm_to_proto(feature_view_source_orm.source),
150
- table_op_graph=feature_view_source_orm.table_op_graph,
163
+ table_op_graph=op,
151
164
  drop_disconnected=feature_view_source_orm.drop_disconnected,
152
165
  org_id=str(feature_view_source_orm.org_id),
153
166
  created_at=timestamp_orm_to_proto(feature_view_source_orm.created_at),
@@ -201,7 +214,7 @@ def space_orm_to_proto(space_orm: orm.Space) -> models_pb2.Space:
201
214
  description=space_orm.description,
202
215
  room_id=str(space_orm.room_id),
203
216
  space_parameters=space_orm.parameters,
204
- feature_view_id=str(space_orm.feature_view_id),
217
+ feature_view=feature_view_orm_to_proto(space_orm.feature_view),
205
218
  auto_sync=space_orm.auto_sync if space_orm.auto_sync is not None else False,
206
219
  org_id=str(space_orm.org_id),
207
220
  created_at=timestamp_orm_to_proto(space_orm.created_at),
@@ -231,27 +244,31 @@ def completion_model_orm_to_proto(
231
244
  )
232
245
 
233
246
 
247
+ def _add_orm_to_session(
248
+ orm_obj: OrmObj, org_id: str, session: sa_orm.Session
249
+ ) -> OrmObj:
250
+ if org_id:
251
+ orm_obj.org_id = orm.OrgID(org_id)
252
+ if not orm_obj.id:
253
+ session.add(orm_obj)
254
+ else:
255
+ orm_obj = session.merge(orm_obj)
256
+ return orm_obj
257
+
258
+
234
259
  def resource_proto_to_orm(
235
260
  proto_obj: models_pb2.Resource, session: sa_orm.Session
236
261
  ) -> Ok[orm.Resource] | InvalidArgumentError:
237
262
  match _translate_orm_ids(proto_obj, orm.ResourceID):
238
263
  case orm.InvalidORMIdentifierError() as err:
239
264
  return err
240
- case Ok(ids):
265
+ case Ok((obj_id, room_id)):
241
266
  pass
242
- if not ids.room_id:
243
- return InvalidArgumentError("room id required to commit resource")
244
-
245
- source_ids = list[orm.SourceID]()
246
- for source_id in proto_obj.source_ids:
247
- orm_id = orm.SourceID(source_id)
248
- match orm_id.to_db():
249
- case orm.InvalidORMIdentifierError() as err:
250
- return err
251
- case Ok():
252
- source_ids.append(orm_id)
267
+ if not room_id:
268
+ return InvalidArgumentError("room id required to commit")
269
+
253
270
  orm_obj = orm.Resource(
254
- id=ids.obj_id,
271
+ id=obj_id,
255
272
  name=proto_obj.name,
256
273
  description=proto_obj.description,
257
274
  mime_type=proto_obj.mime_type,
@@ -260,23 +277,31 @@ def resource_proto_to_orm(
260
277
  size=proto_obj.size,
261
278
  original_path=proto_obj.original_path,
262
279
  latest_event=proto_obj.recent_events[-1] if proto_obj.recent_events else None,
263
- room_id=ids.room_id,
264
- source_associations=[
265
- orm.SourceResourceAssociation(source_id=src_id, resource_id=ids.obj_id)
266
- for src_id in source_ids
267
- ],
280
+ room_id=room_id,
268
281
  )
269
- if proto_obj.org_id:
270
- orm_obj.org_id = orm.OrgID(proto_obj.org_id)
271
- for assn in orm_obj.source_associations:
272
- assn.org_id = orm_obj.org_id
282
+ return Ok(_add_orm_to_session(orm_obj, proto_obj.org_id, session))
273
283
 
274
- if ids.obj_id:
275
- orm_obj = session.merge(orm_obj)
276
- else:
277
- session.add(orm_obj)
278
284
 
279
- return Ok(orm_obj)
285
+ def _ensure_id(
286
+ proto_obj: _Proto,
287
+ proto_to_orm: Callable[[_Proto, sa_orm.Session], Ok[OrmObj] | InvalidArgumentError],
288
+ id_type: type[ID],
289
+ session: sa_orm.Session,
290
+ ) -> Ok[ID] | orm.InvalidORMIdentifierError | InvalidArgumentError:
291
+ match _translate_orm_id(proto_obj.id, id_type):
292
+ case orm.InvalidORMIdentifierError() as err:
293
+ return err
294
+ case Ok(orm_id):
295
+ if orm_id:
296
+ return Ok(orm_id)
297
+ match proto_to_orm(proto_obj, session):
298
+ case orm.InvalidORMIdentifierError() | InvalidArgumentError() as err:
299
+ return err
300
+ case Ok(orm_obj):
301
+ session.flush()
302
+ if not orm_obj.id:
303
+ raise InternalError("internal assertion did not hold")
304
+ return Ok(orm_obj.id)
280
305
 
281
306
 
282
307
  def pipeline_proto_to_orm( # noqa: C901
@@ -285,60 +310,59 @@ def pipeline_proto_to_orm( # noqa: C901
285
310
  match _translate_orm_ids(proto_obj, orm.PipelineID):
286
311
  case orm.InvalidORMIdentifierError() as err:
287
312
  return err
288
- case Ok(ids):
313
+ case Ok((obj_id, room_id)):
289
314
  pass
290
- if not ids.room_id:
291
- return InvalidArgumentError("room id required to commit pipeline")
315
+ if not room_id:
316
+ return InvalidArgumentError("room id required to commit")
292
317
 
318
+ inputs = list[orm.PipelineInput]()
293
319
  orm_obj = orm.Pipeline(
294
- id=ids.obj_id,
320
+ id=obj_id,
295
321
  name=proto_obj.name,
296
- room_id=ids.room_id,
322
+ room_id=room_id,
297
323
  transformation=proto_obj.pipeline_transformation,
298
324
  description=proto_obj.description,
299
325
  )
326
+ orm_obj = _add_orm_to_session(orm_obj, proto_obj.org_id, session)
327
+ session.flush()
300
328
 
301
- if ids.obj_id:
302
- orm_obj = session.merge(orm_obj)
303
- else:
304
- session.add(orm_obj)
329
+ if not orm_obj.id:
330
+ raise InternalError("internal assertion did not hold")
305
331
 
306
332
  for name, val in proto_obj.resource_inputs.items():
307
- if any(input.name == name for input in orm_obj.inputs):
308
- continue
309
- match resource_proto_to_orm(val, session):
333
+ match _ensure_id(val, resource_proto_to_orm, orm.ResourceID, session):
310
334
  case orm.InvalidORMIdentifierError() | InvalidArgumentError() as err:
311
335
  return err
312
- case Ok(resource_orm):
313
- if resource_orm.id:
314
- resource_orm = session.merge(resource_orm)
315
- else:
316
- session.add(resource_orm)
317
- session.merge(
318
- orm.PipelineInput(
319
- pipeline=orm_obj, resource=resource_orm, name=name
320
- )
321
- )
336
+ case Ok(resource_id):
337
+ pass
338
+ inputs.append(
339
+ orm.PipelineInput(
340
+ resource_id=resource_id,
341
+ name=name,
342
+ pipeline_id=orm_obj.id,
343
+ room_id=room_id,
344
+ )
345
+ )
322
346
 
347
+ outputs = list[orm.PipelineOutput]()
323
348
  for name, val in proto_obj.source_outputs.items():
324
- if any(output.name == name for output in orm_obj.outputs):
325
- continue
326
- match source_proto_to_orm(val, session):
349
+ match _ensure_id(val, source_proto_to_orm, orm.SourceID, session):
327
350
  case orm.InvalidORMIdentifierError() | InvalidArgumentError() as err:
328
351
  return err
329
- case Ok(source_orm):
330
- if source_orm.id:
331
- source_orm = session.merge(source_orm)
332
- else:
333
- session.add(source_orm)
334
- session.merge(
335
- orm.PipelineOutput(pipeline=orm_obj, source=source_orm, name=name)
336
- )
352
+ case Ok(source_id):
353
+ pass
354
+ outputs.append(
355
+ orm.PipelineOutput(
356
+ source_id=source_id, name=name, pipeline_id=orm_obj.id, room_id=room_id
357
+ )
358
+ )
359
+
337
360
  if proto_obj.org_id:
338
361
  org_id = orm.OrgID(proto_obj.org_id)
339
- orm_obj.org_id = org_id
340
- for obj in flatten((orm_obj.inputs, orm_obj.outputs)):
341
- obj.org_id = orm.OrgID(proto_obj.org_id)
362
+ for obj in flatten((inputs, outputs)):
363
+ obj.org_id = org_id
364
+ for obj in flatten((inputs, outputs)):
365
+ session.merge(obj)
342
366
  return Ok(orm_obj)
343
367
 
344
368
 
@@ -348,35 +372,18 @@ def source_proto_to_orm(
348
372
  match _translate_orm_ids(proto_obj, orm.SourceID):
349
373
  case orm.InvalidORMIdentifierError() as err:
350
374
  return err
351
- case Ok(ids):
375
+ case Ok((obj_id, room_id)):
352
376
  pass
353
- if not ids.room_id:
354
- return InvalidArgumentError("room id required to commit resource")
355
- resource_id = orm.ResourceID(proto_obj.resource_id)
356
- if resource_id:
357
- associations = [
358
- orm.SourceResourceAssociation(source_id=ids.obj_id, resource_id=resource_id)
359
- ]
360
- else:
361
- associations = list[orm.SourceResourceAssociation]()
377
+ if not room_id:
378
+ return InvalidArgumentError("room id required to commit")
362
379
 
363
380
  orm_obj = orm.Source(
364
- id=ids.obj_id,
381
+ id=obj_id,
365
382
  name=proto_obj.name,
366
383
  table_op_graph=proto_obj.table_op_graph,
367
- room_id=ids.room_id,
368
- resource_associations=associations,
384
+ room_id=room_id,
369
385
  )
370
- if proto_obj.org_id:
371
- orm_obj.org_id = orm.OrgID(proto_obj.org_id)
372
- for assn in orm_obj.resource_associations:
373
- assn.org_id = orm.OrgID(proto_obj.org_id)
374
- if ids.obj_id:
375
- orm_obj = session.merge(orm_obj)
376
- else:
377
- session.add(orm_obj)
378
-
379
- return Ok(orm_obj)
386
+ return Ok(_add_orm_to_session(orm_obj, proto_obj.org_id, session))
380
387
 
381
388
 
382
389
  def agent_proto_to_orm(
@@ -385,26 +392,18 @@ def agent_proto_to_orm(
385
392
  match _translate_orm_ids(proto_obj, orm.AgentID):
386
393
  case orm.InvalidORMIdentifierError() as err:
387
394
  return err
388
- case Ok(ids):
395
+ case Ok((obj_id, room_id)):
389
396
  pass
390
- if not ids.room_id:
391
- return InvalidArgumentError("room id required to commit resource")
397
+ if not room_id:
398
+ return InvalidArgumentError("room id required to commit")
392
399
 
393
400
  orm_obj = orm.Agent(
394
- id=ids.obj_id,
401
+ id=obj_id,
395
402
  name=proto_obj.name,
396
403
  parameters=proto_obj.agent_parameters,
397
- room_id=ids.room_id,
404
+ room_id=room_id,
398
405
  )
399
-
400
- if proto_obj.org_id:
401
- orm_obj.org_id = orm.OrgID(proto_obj.org_id)
402
- if ids.obj_id:
403
- orm_obj = session.merge(orm_obj)
404
- else:
405
- session.add(orm_obj)
406
-
407
- return Ok(orm_obj)
406
+ return Ok(_add_orm_to_session(orm_obj, proto_obj.org_id, session))
408
407
 
409
408
 
410
409
  def space_proto_to_orm(
@@ -413,98 +412,129 @@ def space_proto_to_orm(
413
412
  match _translate_orm_ids(proto_obj, orm.SpaceID):
414
413
  case orm.InvalidORMIdentifierError() as err:
415
414
  return err
416
- case Ok(ids):
415
+ case Ok((obj_id, room_id)):
417
416
  pass
418
- if not ids.room_id:
419
- return InvalidArgumentError("room id required to commit resource")
417
+ if not room_id:
418
+ return InvalidArgumentError("room id required to commit")
420
419
 
421
- feature_view_id = orm.FeatureViewID(proto_obj.feature_view_id)
422
- feature_view = session.get(orm.FeatureView, feature_view_id)
423
- if not feature_view:
424
- return InvalidArgumentError("feature view required to commit resource")
420
+ match _ensure_id(
421
+ proto_obj.feature_view, feature_view_proto_to_orm, orm.FeatureViewID, session
422
+ ):
423
+ case orm.InvalidORMIdentifierError() | InvalidArgumentError() as err:
424
+ return err
425
+ case Ok(feature_view_id):
426
+ pass
427
+
428
+ if not feature_view_id:
429
+ raise InternalError("internal assertion did not hold")
425
430
 
426
431
  orm_obj = orm.Space(
427
- id=ids.obj_id,
432
+ id=obj_id,
428
433
  name=proto_obj.name,
429
434
  description=proto_obj.description,
430
- room_id=ids.room_id,
435
+ room_id=room_id,
431
436
  feature_view_id=feature_view_id,
432
437
  parameters=proto_obj.space_parameters,
433
438
  auto_sync=proto_obj.auto_sync,
434
- feature_view=feature_view,
435
439
  )
436
- if proto_obj.org_id:
437
- orm_obj.org_id = orm.OrgID(proto_obj.org_id)
438
-
439
- if ids.obj_id:
440
- orm_obj = session.merge(orm_obj)
441
- else:
442
- session.add(orm_obj)
443
- return Ok(orm_obj)
440
+ return Ok(_add_orm_to_session(orm_obj, proto_obj.org_id, session))
444
441
 
445
442
 
446
- def feature_view_proto_to_orm(
443
+ def feature_view_proto_to_orm( # noqa: C901
447
444
  proto_obj: models_pb2.FeatureView, session: sa_orm.Session
448
445
  ) -> Ok[orm.FeatureView] | orm.InvalidORMIdentifierError | InvalidArgumentError:
449
446
  match _translate_orm_ids(proto_obj, orm.FeatureViewID):
450
447
  case orm.InvalidORMIdentifierError() as err:
451
448
  return err
452
- case Ok(ids):
449
+ case Ok((obj_id, room_id)):
453
450
  pass
454
- if not ids.room_id:
455
- return InvalidArgumentError("room id required to commit resource")
456
-
457
- feature_view_sources = list[orm.FeatureViewSource]()
458
- for fvs in proto_obj.feature_view_sources:
459
- match feature_view_source_proto_to_orm(fvs, session):
460
- case orm.InvalidORMIdentifierError() | InvalidArgumentError() as err:
461
- return err
462
- case Ok(orm_fvs):
463
- if orm_fvs.id:
464
- orm_fvs = session.merge(orm_fvs)
465
- else:
466
- session.add(orm_fvs)
467
- feature_view_sources.append(orm_fvs)
451
+ if not room_id:
452
+ return InvalidArgumentError("room id required to commit")
468
453
 
469
454
  orm_obj = orm.FeatureView(
470
- id=ids.obj_id,
455
+ id=obj_id,
471
456
  name=proto_obj.name,
472
457
  description=proto_obj.description,
473
- room_id=ids.room_id,
474
- feature_view_output=proto_obj.feature_view_output,
475
- feature_view_sources=feature_view_sources,
458
+ room_id=room_id,
476
459
  )
477
460
  if proto_obj.org_id:
478
461
  orm_obj.org_id = orm.OrgID(proto_obj.org_id)
479
462
 
480
- if ids.obj_id:
481
- orm_obj = session.merge(orm_obj)
482
- else:
483
- session.add(orm_obj)
463
+ orm_obj = _add_orm_to_session(orm_obj, proto_obj.org_id, session)
464
+ session.flush()
465
+
466
+ if not orm_obj.id:
467
+ raise InternalError("internal assertion did not hold")
468
+
469
+ new_fv_sources = list[orm.FeatureViewSource]()
470
+ for fvs in proto_obj.feature_view_sources:
471
+ match _feature_view_source_proto_to_orm(fvs, orm_obj.id, session):
472
+ case orm.InvalidORMIdentifierError() | InvalidArgumentError() as err:
473
+ return err
474
+ case Ok(fvs_orm):
475
+ new_fv_sources.append(fvs_orm)
476
+
477
+ old_to_new_source_id = dict[str, str]()
478
+ for old_fvs, new_fvs in zip(
479
+ proto_obj.feature_view_sources, new_fv_sources, strict=True
480
+ ):
481
+ if old_fvs.source.id != str(new_fvs.source_id):
482
+ old_to_new_source_id[old_fvs.source.id] = str(new_fvs.source_id)
483
+
484
+ orm_obj.feature_view_output = (
485
+ feature_view_pb2.FeatureViewOutput(
486
+ relationships=[
487
+ feature_view_pb2.FeatureViewRelationship(
488
+ start_source_id=old_to_new_source_id.get(
489
+ old_rel.start_source_id, old_rel.start_source_id
490
+ ),
491
+ end_source_id=old_to_new_source_id.get(
492
+ old_rel.end_source_id, old_rel.end_source_id
493
+ ),
494
+ )
495
+ for old_rel in proto_obj.feature_view_output.relationships
496
+ ],
497
+ output_sources=[
498
+ feature_view_pb2.OutputSource(
499
+ source_id=old_to_new_source_id.get(
500
+ old_output_source.source_id, old_output_source.source_id
501
+ )
502
+ )
503
+ for old_output_source in proto_obj.feature_view_output.output_sources
504
+ ],
505
+ )
506
+ if old_to_new_source_id
507
+ else proto_obj.feature_view_output
508
+ )
509
+ orm_obj = session.merge(orm_obj)
484
510
  return Ok(orm_obj)
485
511
 
486
512
 
487
- def feature_view_source_proto_to_orm(
488
- proto_obj: models_pb2.FeatureViewSource, session: sa_orm.Session
513
+ def _feature_view_source_proto_to_orm(
514
+ proto_obj: models_pb2.FeatureViewSource,
515
+ feature_view_id: orm.FeatureViewID,
516
+ session: sa_orm.Session,
489
517
  ) -> Ok[orm.FeatureViewSource] | orm.InvalidORMIdentifierError | InvalidArgumentError:
490
- match source_proto_to_orm(proto_obj.source, session):
518
+ match _ensure_id(proto_obj.source, source_proto_to_orm, orm.SourceID, session):
491
519
  case orm.InvalidORMIdentifierError() | InvalidArgumentError() as err:
492
520
  return err
493
- case Ok(source):
521
+ case Ok(source_id):
522
+ pass
523
+ match _translate_orm_id(proto_obj.id, orm.FeatureViewSourceID):
524
+ case orm.InvalidORMIdentifierError() as err:
525
+ return err
526
+ case Ok(obj_id):
494
527
  pass
495
528
 
496
- if source.id:
497
- source = session.merge(source)
498
- else:
499
- session.add(source)
500
529
  orm_obj = orm.FeatureViewSource(
530
+ room_id=orm.RoomID(proto_obj.room_id),
531
+ id=obj_id,
501
532
  table_op_graph=proto_obj.table_op_graph,
502
533
  drop_disconnected=proto_obj.drop_disconnected,
503
- source=source,
534
+ source_id=source_id,
535
+ feature_view_id=feature_view_id,
504
536
  )
505
- if proto_obj.org_id:
506
- orm_obj.org_id = orm.OrgID(proto_obj.org_id)
507
- return Ok(orm_obj)
537
+ return Ok(_add_orm_to_session(orm_obj, proto_obj.org_id, session))
508
538
 
509
539
 
510
540
  def room_proto_to_orm(
@@ -513,20 +543,14 @@ def room_proto_to_orm(
513
543
  match _translate_orm_ids(proto_obj, orm.RoomID):
514
544
  case orm.InvalidORMIdentifierError() as err:
515
545
  return err
516
- case Ok(ids):
546
+ case Ok((obj_id, _)):
517
547
  pass
518
548
 
519
549
  orm_obj = orm.Room(
520
- id=ids.obj_id,
550
+ id=obj_id,
521
551
  name=proto_obj.name,
522
552
  )
523
-
524
- if ids.obj_id:
525
- orm_obj = session.merge(orm_obj)
526
- else:
527
- session.add(orm_obj)
528
-
529
- return Ok(orm_obj)
553
+ return Ok(_add_orm_to_session(orm_obj, proto_obj.org_id, session))
530
554
 
531
555
 
532
556
  def completion_model_proto_to_orm(
@@ -535,23 +559,17 @@ def completion_model_proto_to_orm(
535
559
  match _translate_orm_ids(proto_obj, orm.CompletionModelID):
536
560
  case orm.InvalidORMIdentifierError() as err:
537
561
  return err
538
- case Ok(ids):
562
+ case Ok((obj_id, _)):
539
563
  pass
540
564
 
541
565
  orm_obj = orm.CompletionModel(
542
- id=ids.obj_id,
566
+ id=obj_id,
543
567
  name=proto_obj.name,
544
568
  description=proto_obj.description,
545
569
  parameters=proto_obj.parameters,
546
570
  secret_api_key=proto_obj.secret_api_key,
547
571
  )
548
-
549
- if ids.obj_id:
550
- orm_obj = session.merge(orm_obj)
551
- else:
552
- session.add(orm_obj)
553
-
554
- return Ok(orm_obj)
572
+ return Ok(_add_orm_to_session(orm_obj, proto_obj.org_id, session))
555
573
 
556
574
 
557
575
  def source_delete_orms(