corvic-engine 0.3.0rc76__cp38-abi3-win_amd64.whl → 0.3.0rc78__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.
- corvic/engine/_native.pyd +0 -0
- corvic/eorm/__init__.py +404 -0
- corvic/model/_base_model.py +44 -40
- corvic/model/_completion_model.py +9 -9
- corvic/model/_defaults.py +5 -5
- corvic/model/_feature_view.py +17 -17
- corvic/model/_pipeline.py +12 -12
- corvic/model/_proto_orm_convert.py +102 -100
- corvic/model/_resource.py +24 -24
- corvic/model/_room.py +10 -14
- corvic/model/_source.py +18 -18
- corvic/model/_space.py +21 -21
- corvic/op_graph/_schema.py +3 -3
- corvic/op_graph/feature_types.py +4 -4
- corvic/op_graph/ops.py +2 -2
- corvic/orm/__init__.py +202 -307
- corvic/orm/_soft_delete.py +218 -0
- corvic/orm/ids.py +0 -69
- corvic/system/_embedder.py +2 -2
- corvic/system/op_graph_executor.py +2 -2
- corvic/system/storage.py +10 -8
- corvic/system_sqlite/client.py +10 -10
- corvic/table/table.py +3 -3
- {corvic_engine-0.3.0rc76.dist-info → corvic_engine-0.3.0rc78.dist-info}/METADATA +1 -1
- {corvic_engine-0.3.0rc76.dist-info → corvic_engine-0.3.0rc78.dist-info}/RECORD +27 -27
- corvic/orm/base.py +0 -256
- corvic/orm/mixins.py +0 -480
- {corvic_engine-0.3.0rc76.dist-info → corvic_engine-0.3.0rc78.dist-info}/WHEEL +0 -0
- {corvic_engine-0.3.0rc76.dist-info → corvic_engine-0.3.0rc78.dist-info}/licenses/LICENSE +0 -0
corvic/engine/_native.pyd
CHANGED
Binary file
|
corvic/eorm/__init__.py
ADDED
@@ -0,0 +1,404 @@
|
|
1
|
+
"""Engine Object-Relational Mappings."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from datetime import datetime
|
6
|
+
from typing import TypeAlias
|
7
|
+
|
8
|
+
import sqlalchemy as sa
|
9
|
+
from sqlalchemy import orm as sa_orm
|
10
|
+
|
11
|
+
from corvic import orm
|
12
|
+
from corvic_generated.orm.v1 import (
|
13
|
+
common_pb2,
|
14
|
+
completion_model_pb2,
|
15
|
+
feature_view_pb2,
|
16
|
+
pipeline_pb2,
|
17
|
+
space_pb2,
|
18
|
+
table_pb2,
|
19
|
+
)
|
20
|
+
from corvic_generated.status.v1 import event_pb2
|
21
|
+
|
22
|
+
OrgID: TypeAlias = orm.OrgID
|
23
|
+
|
24
|
+
|
25
|
+
class RoomID(orm.BaseIDFromInt):
|
26
|
+
"""A unique identifier for a room."""
|
27
|
+
|
28
|
+
|
29
|
+
class ResourceID(orm.BaseIDFromInt):
|
30
|
+
"""A unique identifier for a resource."""
|
31
|
+
|
32
|
+
|
33
|
+
class SourceID(orm.BaseIDFromInt):
|
34
|
+
"""A unique identifier for a source."""
|
35
|
+
|
36
|
+
|
37
|
+
class PipelineID(orm.BaseIDFromInt):
|
38
|
+
"""A unique identifier for a pipeline."""
|
39
|
+
|
40
|
+
|
41
|
+
class FeatureViewID(orm.BaseIDFromInt):
|
42
|
+
"""A unique identifier for a feature view."""
|
43
|
+
|
44
|
+
|
45
|
+
class FeatureViewSourceID(orm.BaseIDFromInt):
|
46
|
+
"""A unique identifier for a source in a feature view."""
|
47
|
+
|
48
|
+
|
49
|
+
class SpaceID(orm.BaseIDFromInt):
|
50
|
+
"""A unique identifier for a space."""
|
51
|
+
|
52
|
+
|
53
|
+
class CompletionModelID(orm.BaseIDFromInt):
|
54
|
+
"""A unique identifier for a completion model."""
|
55
|
+
|
56
|
+
|
57
|
+
orm.Base.registry.update_type_annotation_map(
|
58
|
+
{
|
59
|
+
# proto message column types
|
60
|
+
common_pb2.BlobUrlList: orm.ProtoMessageDecorator(common_pb2.BlobUrlList()),
|
61
|
+
feature_view_pb2.FeatureViewOutput: orm.ProtoMessageDecorator(
|
62
|
+
feature_view_pb2.FeatureViewOutput()
|
63
|
+
),
|
64
|
+
common_pb2.AgentMessageMetadata: orm.ProtoMessageDecorator(
|
65
|
+
common_pb2.AgentMessageMetadata()
|
66
|
+
),
|
67
|
+
space_pb2.SpaceParameters: orm.ProtoMessageDecorator(
|
68
|
+
space_pb2.SpaceParameters()
|
69
|
+
),
|
70
|
+
table_pb2.TableComputeOp: orm.ProtoMessageDecorator(table_pb2.TableComputeOp()),
|
71
|
+
table_pb2.NamedTables: orm.ProtoMessageDecorator(table_pb2.NamedTables()),
|
72
|
+
common_pb2.RetrievedEntities: orm.ProtoMessageDecorator(
|
73
|
+
common_pb2.RetrievedEntities()
|
74
|
+
),
|
75
|
+
pipeline_pb2.PipelineTransformation: orm.ProtoMessageDecorator(
|
76
|
+
pipeline_pb2.PipelineTransformation()
|
77
|
+
),
|
78
|
+
event_pb2.Event: orm.ProtoMessageDecorator(event_pb2.Event()),
|
79
|
+
completion_model_pb2.CompletionModelParameters: orm.ProtoMessageDecorator(
|
80
|
+
completion_model_pb2.CompletionModelParameters()
|
81
|
+
),
|
82
|
+
# ID types
|
83
|
+
RoomID: orm.IntIDDecorator(RoomID()),
|
84
|
+
ResourceID: orm.IntIDDecorator(ResourceID()),
|
85
|
+
SourceID: orm.IntIDDecorator(SourceID()),
|
86
|
+
PipelineID: orm.IntIDDecorator(PipelineID()),
|
87
|
+
FeatureViewID: orm.IntIDDecorator(FeatureViewID()),
|
88
|
+
FeatureViewSourceID: orm.IntIDDecorator(FeatureViewSourceID()),
|
89
|
+
SpaceID: orm.IntIDDecorator(SpaceID()),
|
90
|
+
CompletionModelID: orm.IntIDDecorator(CompletionModelID()),
|
91
|
+
}
|
92
|
+
)
|
93
|
+
|
94
|
+
|
95
|
+
class Room(orm.BelongsToOrgMixin, orm.SoftDeleteMixin, orm.Base, kw_only=True):
|
96
|
+
"""A Room is a logical collection of Documents."""
|
97
|
+
|
98
|
+
__tablename__ = "room"
|
99
|
+
__table_args__ = (orm.live_unique_constraint("name", "org_id"),)
|
100
|
+
|
101
|
+
name: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text, default=None)
|
102
|
+
id: sa_orm.Mapped[RoomID | None] = orm.primary_key_identity_column()
|
103
|
+
|
104
|
+
@property
|
105
|
+
def room_key(self):
|
106
|
+
return self.name
|
107
|
+
|
108
|
+
|
109
|
+
class BelongsToRoomMixin(sa_orm.MappedAsDataclass):
|
110
|
+
room_id: sa_orm.Mapped[RoomID | None] = sa_orm.mapped_column(
|
111
|
+
orm.ForeignKey(Room).make(ondelete="CASCADE"),
|
112
|
+
nullable=True,
|
113
|
+
default=None,
|
114
|
+
)
|
115
|
+
|
116
|
+
|
117
|
+
class DefaultObjects(orm.Base, kw_only=True):
|
118
|
+
"""Holds the identifiers for default objects."""
|
119
|
+
|
120
|
+
__tablename__ = "default_objects"
|
121
|
+
default_org: sa_orm.Mapped[OrgID | None] = sa_orm.mapped_column(
|
122
|
+
orm.ForeignKey(orm.Org).make(ondelete="CASCADE"),
|
123
|
+
nullable=False,
|
124
|
+
)
|
125
|
+
default_room: sa_orm.Mapped[RoomID | None] = sa_orm.mapped_column(
|
126
|
+
orm.ForeignKey(Room).make(ondelete="CASCADE"), nullable=True, default=None
|
127
|
+
)
|
128
|
+
version: sa_orm.Mapped[int | None] = orm.primary_key_identity_column(
|
129
|
+
type_=orm.INT_PK_TYPE
|
130
|
+
)
|
131
|
+
|
132
|
+
|
133
|
+
class Resource(orm.BelongsToOrgMixin, BelongsToRoomMixin, orm.Base, kw_only=True):
|
134
|
+
"""A Resource is a reference to some durably stored file.
|
135
|
+
|
136
|
+
E.g., a document could be a PDF file, an image, or a text transcript of a
|
137
|
+
conversation
|
138
|
+
"""
|
139
|
+
|
140
|
+
__tablename__ = "resource"
|
141
|
+
|
142
|
+
name: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text)
|
143
|
+
mime_type: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text)
|
144
|
+
url: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text)
|
145
|
+
md5: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.CHAR(32), nullable=True)
|
146
|
+
size: sa_orm.Mapped[int] = sa_orm.mapped_column(nullable=True)
|
147
|
+
original_path: sa_orm.Mapped[str] = sa_orm.mapped_column(nullable=True)
|
148
|
+
description: sa_orm.Mapped[str] = sa_orm.mapped_column(nullable=True)
|
149
|
+
id: sa_orm.Mapped[ResourceID | None] = orm.primary_key_identity_column()
|
150
|
+
latest_event: sa_orm.Mapped[event_pb2.Event | None] = sa_orm.mapped_column(
|
151
|
+
default=None, nullable=True
|
152
|
+
)
|
153
|
+
is_terminal: sa_orm.Mapped[bool | None] = sa_orm.mapped_column(
|
154
|
+
default=None, nullable=True
|
155
|
+
)
|
156
|
+
pipeline_ref: sa_orm.Mapped[PipelineInput | None] = sa_orm.relationship(
|
157
|
+
init=False, viewonly=True
|
158
|
+
)
|
159
|
+
|
160
|
+
|
161
|
+
class Source(orm.BelongsToOrgMixin, BelongsToRoomMixin, orm.Base, kw_only=True):
|
162
|
+
"""A source."""
|
163
|
+
|
164
|
+
__tablename__ = "source"
|
165
|
+
__table_args__ = (sa.UniqueConstraint("name", "room_id"),)
|
166
|
+
|
167
|
+
name: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text)
|
168
|
+
# protobuf describing the operations required to construct a table
|
169
|
+
table_op_graph: sa_orm.Mapped[table_pb2.TableComputeOp] = sa_orm.mapped_column()
|
170
|
+
id: sa_orm.Mapped[SourceID | None] = orm.primary_key_identity_column()
|
171
|
+
|
172
|
+
source_files: sa_orm.Mapped[common_pb2.BlobUrlList | None] = sa_orm.mapped_column(
|
173
|
+
default=None
|
174
|
+
)
|
175
|
+
pipeline_ref: sa_orm.Mapped[PipelineOutput | None] = sa_orm.relationship(
|
176
|
+
init=False, viewonly=True
|
177
|
+
)
|
178
|
+
|
179
|
+
@property
|
180
|
+
def source_key(self):
|
181
|
+
return self.name
|
182
|
+
|
183
|
+
|
184
|
+
class Pipeline(orm.BelongsToOrgMixin, BelongsToRoomMixin, orm.Base, kw_only=True):
|
185
|
+
"""A resource to source pipeline."""
|
186
|
+
|
187
|
+
__tablename__ = "pipeline"
|
188
|
+
__table_args__ = (sa.UniqueConstraint("name", "room_id"),)
|
189
|
+
|
190
|
+
transformation: sa_orm.Mapped[pipeline_pb2.PipelineTransformation] = (
|
191
|
+
sa_orm.mapped_column()
|
192
|
+
)
|
193
|
+
name: sa_orm.Mapped[str] = sa_orm.mapped_column()
|
194
|
+
description: sa_orm.Mapped[str | None] = sa_orm.mapped_column()
|
195
|
+
id: sa_orm.Mapped[PipelineID | None] = orm.primary_key_identity_column()
|
196
|
+
|
197
|
+
outputs: sa_orm.Mapped[list[PipelineOutput]] = sa_orm.relationship(
|
198
|
+
viewonly=True,
|
199
|
+
init=False,
|
200
|
+
default_factory=list,
|
201
|
+
)
|
202
|
+
|
203
|
+
|
204
|
+
class PipelineInput(orm.BelongsToOrgMixin, BelongsToRoomMixin, orm.Base, kw_only=True):
|
205
|
+
"""Pipeline input resources."""
|
206
|
+
|
207
|
+
__tablename__ = "pipeline_input"
|
208
|
+
__table_args__ = (sa.UniqueConstraint("name", "pipeline_id"),)
|
209
|
+
|
210
|
+
resource: sa_orm.Mapped[Resource] = sa_orm.relationship(viewonly=True, init=False)
|
211
|
+
name: sa_orm.Mapped[str]
|
212
|
+
"""A name the pipeline uses to refer to this input."""
|
213
|
+
|
214
|
+
pipeline_id: sa_orm.Mapped[PipelineID] = orm.primary_key_foreign_column(
|
215
|
+
orm.ForeignKey(Pipeline).make(ondelete="CASCADE")
|
216
|
+
)
|
217
|
+
resource_id: sa_orm.Mapped[ResourceID] = orm.primary_key_foreign_column(
|
218
|
+
orm.ForeignKey(Resource).make(ondelete="CASCADE")
|
219
|
+
)
|
220
|
+
|
221
|
+
|
222
|
+
class PipelineOutput(orm.BelongsToOrgMixin, BelongsToRoomMixin, orm.Base, kw_only=True):
|
223
|
+
"""Objects for tracking pipeline output sources."""
|
224
|
+
|
225
|
+
__tablename__ = "pipeline_output"
|
226
|
+
__table_args__ = (sa.UniqueConstraint("name", "pipeline_id"),)
|
227
|
+
|
228
|
+
source: sa_orm.Mapped[Source] = sa_orm.relationship(viewonly=True, init=False)
|
229
|
+
name: sa_orm.Mapped[str]
|
230
|
+
"""A name the pipeline uses to refer to this output."""
|
231
|
+
|
232
|
+
pipeline_id: sa_orm.Mapped[PipelineID] = orm.primary_key_foreign_column(
|
233
|
+
orm.ForeignKey(Pipeline).make(ondelete="CASCADE")
|
234
|
+
)
|
235
|
+
source_id: sa_orm.Mapped[SourceID] = orm.primary_key_foreign_column(
|
236
|
+
orm.ForeignKey(Source).make(ondelete="CASCADE")
|
237
|
+
)
|
238
|
+
|
239
|
+
|
240
|
+
class FeatureView(
|
241
|
+
orm.SoftDeleteMixin,
|
242
|
+
orm.BelongsToOrgMixin,
|
243
|
+
BelongsToRoomMixin,
|
244
|
+
orm.Base,
|
245
|
+
kw_only=True,
|
246
|
+
):
|
247
|
+
"""A FeatureView is a logical collection of sources used by various spaces."""
|
248
|
+
|
249
|
+
__tablename__ = "feature_view"
|
250
|
+
__table_args__ = (orm.live_unique_constraint("name", "room_id"),)
|
251
|
+
|
252
|
+
id: sa_orm.Mapped[FeatureViewID | None] = orm.primary_key_identity_column()
|
253
|
+
name: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text, default=None)
|
254
|
+
description: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text, default="")
|
255
|
+
|
256
|
+
feature_view_output: sa_orm.Mapped[feature_view_pb2.FeatureViewOutput | None] = (
|
257
|
+
sa_orm.mapped_column(default_factory=feature_view_pb2.FeatureViewOutput)
|
258
|
+
)
|
259
|
+
|
260
|
+
@property
|
261
|
+
def feature_view_key(self):
|
262
|
+
return self.name
|
263
|
+
|
264
|
+
feature_view_sources: sa_orm.Mapped[list[FeatureViewSource]] = sa_orm.relationship(
|
265
|
+
viewonly=True,
|
266
|
+
init=False,
|
267
|
+
default_factory=list,
|
268
|
+
)
|
269
|
+
|
270
|
+
|
271
|
+
class FeatureViewSource(
|
272
|
+
orm.BelongsToOrgMixin, BelongsToRoomMixin, orm.Base, kw_only=True
|
273
|
+
):
|
274
|
+
"""A source inside of a feature view."""
|
275
|
+
|
276
|
+
__tablename__ = "feature_view_source"
|
277
|
+
|
278
|
+
table_op_graph: sa_orm.Mapped[table_pb2.TableComputeOp] = sa_orm.mapped_column()
|
279
|
+
feature_view_id: sa_orm.Mapped[FeatureViewID] = sa_orm.mapped_column(
|
280
|
+
orm.ForeignKey(FeatureView).make(ondelete="CASCADE"),
|
281
|
+
nullable=False,
|
282
|
+
)
|
283
|
+
id: sa_orm.Mapped[FeatureViewSourceID | None] = orm.primary_key_identity_column()
|
284
|
+
drop_disconnected: sa_orm.Mapped[bool] = sa_orm.mapped_column(default=False)
|
285
|
+
# this should be legal but pyright complains that it makes Source depend
|
286
|
+
# on itself
|
287
|
+
source_id: sa_orm.Mapped[SourceID] = sa_orm.mapped_column(
|
288
|
+
orm.ForeignKey(Source).make(ondelete="CASCADE"),
|
289
|
+
nullable=False,
|
290
|
+
default=None,
|
291
|
+
)
|
292
|
+
source: sa_orm.Mapped[Source] = sa_orm.relationship(
|
293
|
+
init=True, viewonly=True, default=None
|
294
|
+
)
|
295
|
+
|
296
|
+
|
297
|
+
class Space(orm.BelongsToOrgMixin, BelongsToRoomMixin, orm.Base, kw_only=True):
|
298
|
+
"""A space is a named evaluation of space parameters."""
|
299
|
+
|
300
|
+
__tablename__ = "space"
|
301
|
+
__table_args__ = (sa.UniqueConstraint("name", "room_id"),)
|
302
|
+
|
303
|
+
id: sa_orm.Mapped[SpaceID | None] = orm.primary_key_identity_column()
|
304
|
+
name: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text, default=None)
|
305
|
+
description: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text, default="")
|
306
|
+
|
307
|
+
feature_view_id: sa_orm.Mapped[FeatureViewID] = sa_orm.mapped_column(
|
308
|
+
orm.ForeignKey(FeatureView).make(ondelete="CASCADE"),
|
309
|
+
nullable=False,
|
310
|
+
default=None,
|
311
|
+
)
|
312
|
+
parameters: sa_orm.Mapped[space_pb2.SpaceParameters | None] = sa_orm.mapped_column(
|
313
|
+
default=None
|
314
|
+
)
|
315
|
+
auto_sync: sa_orm.Mapped[bool | None] = sa_orm.mapped_column(default=None)
|
316
|
+
feature_view: sa_orm.Mapped[FeatureView] = sa_orm.relationship(
|
317
|
+
init=False,
|
318
|
+
default=None,
|
319
|
+
viewonly=True,
|
320
|
+
)
|
321
|
+
|
322
|
+
@property
|
323
|
+
def space_key(self):
|
324
|
+
return self.name
|
325
|
+
|
326
|
+
|
327
|
+
class CompletionModel(
|
328
|
+
orm.SoftDeleteMixin, orm.BelongsToOrgMixin, orm.Base, kw_only=True
|
329
|
+
):
|
330
|
+
"""A customer's custom completion model definition."""
|
331
|
+
|
332
|
+
__tablename__ = "completion_model"
|
333
|
+
__table_args__ = (orm.live_unique_constraint("name", "org_id"),)
|
334
|
+
|
335
|
+
id: sa_orm.Mapped[CompletionModelID | None] = orm.primary_key_identity_column()
|
336
|
+
name: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text, default=None)
|
337
|
+
description: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text, default=None)
|
338
|
+
parameters: sa_orm.Mapped[completion_model_pb2.CompletionModelParameters | None] = (
|
339
|
+
sa_orm.mapped_column(default=None)
|
340
|
+
)
|
341
|
+
secret_api_key: sa_orm.Mapped[str] = sa_orm.mapped_column(sa.Text, default=None)
|
342
|
+
last_validation_time: sa_orm.Mapped[datetime | None] = sa_orm.mapped_column(
|
343
|
+
sa.DateTime(timezone=True),
|
344
|
+
server_default=None,
|
345
|
+
default=None,
|
346
|
+
)
|
347
|
+
last_successful_validation: sa_orm.Mapped[datetime | None] = sa_orm.mapped_column(
|
348
|
+
sa.DateTime(timezone=True),
|
349
|
+
server_default=None,
|
350
|
+
default=None,
|
351
|
+
)
|
352
|
+
|
353
|
+
@property
|
354
|
+
def model_key(self):
|
355
|
+
return self.name
|
356
|
+
|
357
|
+
|
358
|
+
ID = (
|
359
|
+
orm.ID
|
360
|
+
| CompletionModelID
|
361
|
+
| FeatureViewID
|
362
|
+
| FeatureViewSourceID
|
363
|
+
| PipelineID
|
364
|
+
| ResourceID
|
365
|
+
| RoomID
|
366
|
+
| SourceID
|
367
|
+
| SpaceID
|
368
|
+
)
|
369
|
+
|
370
|
+
# These are part of the public interface, exposing them here so that
|
371
|
+
# users don't have to import orm anytime they import eorm
|
372
|
+
Base: TypeAlias = orm.Base
|
373
|
+
Org: TypeAlias = orm.Org
|
374
|
+
Session: TypeAlias = orm.Session
|
375
|
+
|
376
|
+
InvalidORMIdentifierError: TypeAlias = orm.InvalidORMIdentifierError
|
377
|
+
RequestedObjectsForNobodyError: TypeAlias = orm.RequestedObjectsForNobodyError
|
378
|
+
|
379
|
+
__all__ = [
|
380
|
+
"Base",
|
381
|
+
"CompletionModel",
|
382
|
+
"CompletionModelID",
|
383
|
+
"DefaultObjects",
|
384
|
+
"FeatureView",
|
385
|
+
"FeatureViewID",
|
386
|
+
"FeatureViewSource",
|
387
|
+
"FeatureViewSourceID",
|
388
|
+
"ID",
|
389
|
+
"InvalidORMIdentifierError",
|
390
|
+
"OrgID",
|
391
|
+
"PipelineID",
|
392
|
+
"PipelineInput",
|
393
|
+
"PipelineOutput",
|
394
|
+
"RequestedObjectsForNobodyError",
|
395
|
+
"Resource",
|
396
|
+
"ResourceID",
|
397
|
+
"Room",
|
398
|
+
"RoomID",
|
399
|
+
"Session",
|
400
|
+
"Source",
|
401
|
+
"SourceID",
|
402
|
+
"Space",
|
403
|
+
"SpaceID",
|
404
|
+
]
|
corvic/model/_base_model.py
CHANGED
@@ -12,10 +12,10 @@ import sqlalchemy.orm as sa_orm
|
|
12
12
|
import structlog
|
13
13
|
from google.protobuf import timestamp_pb2
|
14
14
|
|
15
|
-
from corvic import
|
15
|
+
from corvic import eorm, system
|
16
16
|
from corvic.model._proto_orm_convert import (
|
17
|
-
ID,
|
18
17
|
UNCOMMITTED_ID_PREFIX,
|
18
|
+
IdType,
|
19
19
|
OrmBelongsToOrgObj,
|
20
20
|
OrmBelongsToRoomObj,
|
21
21
|
OrmObj,
|
@@ -54,7 +54,7 @@ def _create_or_join_session(
|
|
54
54
|
if existing_session:
|
55
55
|
yield existing_session
|
56
56
|
else:
|
57
|
-
with
|
57
|
+
with eorm.Session(client.sa_engine) as session:
|
58
58
|
yield session
|
59
59
|
|
60
60
|
|
@@ -71,7 +71,7 @@ class HasProtoSelf(Generic[ProtoObj], abc.ABC):
|
|
71
71
|
return non_empty_timestamp_to_datetime(self.proto_self.created_at)
|
72
72
|
|
73
73
|
|
74
|
-
class UsesOrmID(Generic[
|
74
|
+
class UsesOrmID(Generic[IdType, ProtoObj], HasProtoSelf[ProtoObj]):
|
75
75
|
def __init__(self, client: system.Client, proto_self: ProtoObj):
|
76
76
|
if not proto_self.id:
|
77
77
|
proto_self.id = _generate_uncommitted_id_str()
|
@@ -79,14 +79,14 @@ class UsesOrmID(Generic[ID, ProtoObj], HasProtoSelf[ProtoObj]):
|
|
79
79
|
|
80
80
|
@classmethod
|
81
81
|
@abc.abstractmethod
|
82
|
-
def id_class(cls) -> type[
|
82
|
+
def id_class(cls) -> type[IdType]: ...
|
83
83
|
|
84
84
|
@functools.cached_property
|
85
|
-
def id(self) ->
|
85
|
+
def id(self) -> IdType:
|
86
86
|
return self.id_class().from_str(self.proto_self.id)
|
87
87
|
|
88
88
|
|
89
|
-
class BaseModel(Generic[
|
89
|
+
class BaseModel(Generic[IdType, ProtoObj, OrmObj], UsesOrmID[IdType, ProtoObj]):
|
90
90
|
"""Base for orm wrappers providing a unified update mechanism."""
|
91
91
|
|
92
92
|
@property
|
@@ -104,19 +104,19 @@ class BaseModel(Generic[ID, ProtoObj, OrmObj], UsesOrmID[ID, ProtoObj]):
|
|
104
104
|
@classmethod
|
105
105
|
@abc.abstractmethod
|
106
106
|
def proto_to_orm(
|
107
|
-
cls, proto_obj: ProtoObj, session:
|
107
|
+
cls, proto_obj: ProtoObj, session: eorm.Session
|
108
108
|
) -> Ok[OrmObj] | InvalidArgumentError: ...
|
109
109
|
|
110
110
|
@classmethod
|
111
111
|
@abc.abstractmethod
|
112
112
|
def delete_by_ids(
|
113
|
-
cls, ids: Sequence[
|
113
|
+
cls, ids: Sequence[IdType], session: eorm.Session
|
114
114
|
) -> Ok[None] | InvalidArgumentError: ...
|
115
115
|
|
116
116
|
@classmethod
|
117
117
|
def load_proto_for(
|
118
118
|
cls,
|
119
|
-
obj_id:
|
119
|
+
obj_id: IdType,
|
120
120
|
client: system.Client,
|
121
121
|
existing_session: sa_orm.Session | None = None,
|
122
122
|
) -> Ok[ProtoObj] | NotFoundError:
|
@@ -155,9 +155,9 @@ class BaseModel(Generic[ID, ProtoObj, OrmObj], UsesOrmID[ID, ProtoObj]):
|
|
155
155
|
client: system.Client,
|
156
156
|
*,
|
157
157
|
limit: int | None = None,
|
158
|
-
room_id:
|
158
|
+
room_id: eorm.RoomID | None = None,
|
159
159
|
created_before: datetime.datetime | None = None,
|
160
|
-
ids: Iterable[
|
160
|
+
ids: Iterable[IdType] | None = None,
|
161
161
|
additional_query_transform: Callable[
|
162
162
|
[sa.Select[tuple[OrmObj]]], sa.Select[tuple[OrmObj]]
|
163
163
|
]
|
@@ -173,7 +173,7 @@ class BaseModel(Generic[ID, ProtoObj, OrmObj], UsesOrmID[ID, ProtoObj]):
|
|
173
173
|
return InvalidArgumentError("limit cannot be negative")
|
174
174
|
query = query.limit(limit)
|
175
175
|
if room_id:
|
176
|
-
if session.get(
|
176
|
+
if session.get(eorm.Room, room_id) is None:
|
177
177
|
return NotFoundError("room not found", room_id=room_id)
|
178
178
|
query = query.filter_by(room_id=room_id)
|
179
179
|
if created_before:
|
@@ -198,20 +198,14 @@ class BaseModel(Generic[ID, ProtoObj, OrmObj], UsesOrmID[ID, ProtoObj]):
|
|
198
198
|
This overwrites the entry at id in the database so that future readers will see
|
199
199
|
this object. One of `id` or `derived_from_id` cannot be empty or None.
|
200
200
|
"""
|
201
|
-
with
|
201
|
+
with eorm.Session(self.client.sa_engine) as session:
|
202
202
|
try:
|
203
203
|
new_orm_self = self.proto_to_orm(
|
204
204
|
self.proto_self, session
|
205
205
|
).unwrap_or_raise()
|
206
206
|
session.commit()
|
207
|
-
except sa.exc.
|
208
|
-
|
209
|
-
return InvalidArgumentError.from_(err)
|
210
|
-
if "could not serialize access due to concurrent update" in str(err):
|
211
|
-
return UnavailableError.from_(err)
|
212
|
-
return InvalidArgumentError.from_(err)
|
213
|
-
except (sa.exc.DatabaseError, sa.exc.StatementError) as err:
|
214
|
-
return InvalidArgumentError.from_(err)
|
207
|
+
except sa.exc.DBAPIError as err:
|
208
|
+
return self._dbapi_error_to_result(err)
|
215
209
|
return Ok(
|
216
210
|
self.__class__(
|
217
211
|
client=self.client,
|
@@ -219,8 +213,24 @@ class BaseModel(Generic[ID, ProtoObj, OrmObj], UsesOrmID[ID, ProtoObj]):
|
|
219
213
|
)
|
220
214
|
)
|
221
215
|
|
216
|
+
@staticmethod
|
217
|
+
def _dbapi_error_to_result(err: sa.exc.DBAPIError):
|
218
|
+
try:
|
219
|
+
import psycopg.errors
|
220
|
+
|
221
|
+
if isinstance(err.orig, psycopg.errors.SerializationFailure):
|
222
|
+
return UnavailableError.from_(err)
|
223
|
+
except ModuleNotFoundError:
|
224
|
+
pass
|
225
|
+
if isinstance(err, sa.exc.ProgrammingError):
|
226
|
+
if "violates foreign key constraint" in str(err):
|
227
|
+
return InvalidArgumentError.from_(err)
|
228
|
+
if "could not serialize access due to concurrent update" in str(err):
|
229
|
+
return UnavailableError.from_(err)
|
230
|
+
return InvalidArgumentError.from_(err)
|
231
|
+
|
222
232
|
def add_to_session(
|
223
|
-
self, session:
|
233
|
+
self, session: eorm.Session
|
224
234
|
) -> Ok[None] | InvalidArgumentError | UnavailableError:
|
225
235
|
"""Like commit, but just calls session.flush to check for database errors.
|
226
236
|
|
@@ -232,18 +242,12 @@ class BaseModel(Generic[ID, ProtoObj, OrmObj], UsesOrmID[ID, ProtoObj]):
|
|
232
242
|
_ = self.proto_to_orm(self.proto_self, session).unwrap_or_raise()
|
233
243
|
session.flush()
|
234
244
|
# TODO(thunt): Possibly separate out DatabaseError into a precondition error
|
235
|
-
except sa.exc.
|
236
|
-
|
237
|
-
return InvalidArgumentError.from_(err)
|
238
|
-
if "could not serialize access due to concurrent update" in str(err):
|
239
|
-
return UnavailableError.from_(err)
|
240
|
-
return InvalidArgumentError.from_(err)
|
241
|
-
except (sa.exc.DatabaseError, sa.exc.StatementError) as err:
|
242
|
-
return InvalidArgumentError.from_(err)
|
245
|
+
except sa.exc.DBAPIError as err:
|
246
|
+
return self._dbapi_error_to_result(err)
|
243
247
|
return Ok(None)
|
244
248
|
|
245
249
|
def delete(self) -> Ok[Self] | NotFoundError | InvalidArgumentError:
|
246
|
-
with
|
250
|
+
with eorm.Session(
|
247
251
|
self.client.sa_engine, expire_on_commit=False, autoflush=False
|
248
252
|
) as session:
|
249
253
|
try:
|
@@ -268,22 +272,22 @@ class BaseModel(Generic[ID, ProtoObj, OrmObj], UsesOrmID[ID, ProtoObj]):
|
|
268
272
|
|
269
273
|
|
270
274
|
class BelongsToOrgModel(
|
271
|
-
Generic[
|
272
|
-
BaseModel[
|
275
|
+
Generic[IdType, ProtoBelongsToOrgObj, OrmBelongsToOrgObj],
|
276
|
+
BaseModel[IdType, ProtoBelongsToOrgObj, OrmBelongsToOrgObj],
|
273
277
|
):
|
274
278
|
"""Base for orm wrappers with org mixin providing a unified update mechanism."""
|
275
279
|
|
276
280
|
@property
|
277
|
-
def org_id(self) ->
|
278
|
-
return
|
281
|
+
def org_id(self) -> eorm.OrgID:
|
282
|
+
return eorm.OrgID().from_str(self.proto_self.org_id)
|
279
283
|
|
280
284
|
|
281
285
|
class BelongsToRoomModel(
|
282
|
-
Generic[
|
283
|
-
BelongsToOrgModel[
|
286
|
+
Generic[IdType, ProtoBelongsToRoomObj, OrmBelongsToRoomObj],
|
287
|
+
BelongsToOrgModel[IdType, ProtoBelongsToRoomObj, OrmBelongsToRoomObj],
|
284
288
|
):
|
285
289
|
"""Base for orm wrappers with room mixin providing a unified update mechanism."""
|
286
290
|
|
287
291
|
@property
|
288
|
-
def room_id(self) ->
|
289
|
-
return
|
292
|
+
def room_id(self) -> eorm.RoomID:
|
293
|
+
return eorm.RoomID().from_str(self.proto_self.room_id)
|
@@ -9,7 +9,7 @@ from typing import Literal, TypeAlias
|
|
9
9
|
|
10
10
|
from sqlalchemy import orm as sa_orm
|
11
11
|
|
12
|
-
from corvic import
|
12
|
+
from corvic import eorm, system
|
13
13
|
from corvic.model._base_model import BelongsToOrgModel, non_empty_timestamp_to_datetime
|
14
14
|
from corvic.model._defaults import Defaults
|
15
15
|
from corvic.model._proto_orm_convert import (
|
@@ -21,38 +21,38 @@ from corvic.result import InvalidArgumentError, NotFoundError, Ok
|
|
21
21
|
from corvic_generated.model.v1alpha import models_pb2
|
22
22
|
from corvic_generated.orm.v1 import completion_model_pb2
|
23
23
|
|
24
|
-
CompletionModelID: TypeAlias =
|
25
|
-
OrgID: TypeAlias =
|
24
|
+
CompletionModelID: TypeAlias = eorm.CompletionModelID
|
25
|
+
OrgID: TypeAlias = eorm.OrgID
|
26
26
|
|
27
27
|
|
28
28
|
class CompletionModel(
|
29
29
|
BelongsToOrgModel[
|
30
|
-
CompletionModelID, models_pb2.CompletionModel,
|
30
|
+
CompletionModelID, models_pb2.CompletionModel, eorm.CompletionModel
|
31
31
|
]
|
32
32
|
):
|
33
33
|
"""Completion Models."""
|
34
34
|
|
35
35
|
@classmethod
|
36
36
|
def orm_class(cls):
|
37
|
-
return
|
37
|
+
return eorm.CompletionModel
|
38
38
|
|
39
39
|
@classmethod
|
40
40
|
def id_class(cls):
|
41
41
|
return CompletionModelID
|
42
42
|
|
43
43
|
@classmethod
|
44
|
-
def orm_to_proto(cls, orm_obj:
|
44
|
+
def orm_to_proto(cls, orm_obj: eorm.CompletionModel) -> models_pb2.CompletionModel:
|
45
45
|
return completion_model_orm_to_proto(orm_obj)
|
46
46
|
|
47
47
|
@classmethod
|
48
48
|
def proto_to_orm(
|
49
|
-
cls, proto_obj: models_pb2.CompletionModel, session:
|
50
|
-
) -> Ok[
|
49
|
+
cls, proto_obj: models_pb2.CompletionModel, session: eorm.Session
|
50
|
+
) -> Ok[eorm.CompletionModel] | InvalidArgumentError:
|
51
51
|
return completion_model_proto_to_orm(proto_obj, session)
|
52
52
|
|
53
53
|
@classmethod
|
54
54
|
def delete_by_ids(
|
55
|
-
cls, ids: Sequence[CompletionModelID], session:
|
55
|
+
cls, ids: Sequence[CompletionModelID], session: eorm.Session
|
56
56
|
) -> Ok[None] | InvalidArgumentError:
|
57
57
|
return completion_model_delete_orms(ids, session)
|
58
58
|
|
corvic/model/_defaults.py
CHANGED
@@ -6,7 +6,7 @@ import tempfile
|
|
6
6
|
|
7
7
|
import sqlalchemy as sa
|
8
8
|
|
9
|
-
from corvic import
|
9
|
+
from corvic import eorm, system, system_sqlite
|
10
10
|
from corvic.result import NotFoundError
|
11
11
|
|
12
12
|
|
@@ -33,11 +33,11 @@ class Defaults:
|
|
33
33
|
return _default_default_client()
|
34
34
|
|
35
35
|
@staticmethod
|
36
|
-
def get_default_room_id(client: system.Client) ->
|
37
|
-
with
|
36
|
+
def get_default_room_id(client: system.Client) -> eorm.RoomID:
|
37
|
+
with eorm.Session(client.sa_engine) as session:
|
38
38
|
defaults_row = session.scalars(
|
39
|
-
sa.select(
|
40
|
-
.order_by(
|
39
|
+
sa.select(eorm.DefaultObjects)
|
40
|
+
.order_by(eorm.DefaultObjects.version.desc())
|
41
41
|
.limit(1)
|
42
42
|
).one_or_none()
|
43
43
|
if not defaults_row or not defaults_row.default_room:
|