lorebase-store 0.2.3__py3-none-any.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.
Files changed (163) hide show
  1. lorebase_store/__init__.py +1 -0
  2. lorebase_store/_migrations/env.py +106 -0
  3. lorebase_store/_migrations/script.py.mako +27 -0
  4. lorebase_store/_migrations/versions/0001_initial_schema.py +1071 -0
  5. lorebase_store/_migrations/versions/0002_merge_attempt_details.py +74 -0
  6. lorebase_store/_migrations/versions/0003_webhooks.py +127 -0
  7. lorebase_store/_migrations/versions/0004_defer_revision_references.py +38 -0
  8. lorebase_store/_migrations/versions/0005_security_definer_reads.py +396 -0
  9. lorebase_store/_migrations/versions/0006_api_key_usage.py +48 -0
  10. lorebase_store/_migrations/versions/0007_document_extraction.py +154 -0
  11. lorebase_store/_migrations/versions/0008_definer_argument_checks.py +178 -0
  12. lorebase_store/_migrations/versions/0009_api_key_networks.py +33 -0
  13. lorebase_store/_migrations/versions/0010_overlay_branches.py +582 -0
  14. lorebase_store/_migrations/versions/0011_branch_scoped_search_index.py +229 -0
  15. lorebase_store/_migrations/versions/0012_overlay_neighborhood_lookups.py +185 -0
  16. lorebase_store/_migrations/versions/0013_web_sessions.py +72 -0
  17. lorebase_store/_migrations/versions/0014_embedding_jobs.py +70 -0
  18. lorebase_store/_migrations/versions/0015_append_only_audit_log.py +83 -0
  19. lorebase_store/_migrations/versions/0016_sealed_webhook_keys.py +55 -0
  20. lorebase_store/_migrations/versions/0017_background_branch_copies.py +77 -0
  21. lorebase_store/_migrations/versions/0018_search_lexicon.py +477 -0
  22. lorebase_store/_migrations/versions/0019_vault_bindings.py +56 -0
  23. lorebase_store/_migrations/versions/0020_outbox_consumers.py +150 -0
  24. lorebase_store/_migrations/versions/0021_schema_suggestions.py +114 -0
  25. lorebase_store/_migrations/versions/0022_read_audit_and_shared_rate_limits.py +133 -0
  26. lorebase_store/_migrations/versions/0023_agent_trace.py +96 -0
  27. lorebase_store/_migrations/versions/0024_embedding_template_keys.py +205 -0
  28. lorebase_store/_migrations/versions/0025_snapshot_refs.py +35 -0
  29. lorebase_store/_migrations/versions/0026_plain_reverse_link_index.py +112 -0
  30. lorebase_store/_migrations/versions/0027_vault_rotation_and_tombstones.py +145 -0
  31. lorebase_store/_migrations/versions/0028_operation_deferred_events.py +71 -0
  32. lorebase_store/_migrations/versions/0029_agent_trace_sessions.py +108 -0
  33. lorebase_store/_migrations/versions/0030_path_fanout.py +31 -0
  34. lorebase_store/_migrations/versions/0031_branch_builds.py +91 -0
  35. lorebase_store/_migrations/versions/0032_lineage_revisions.py +35 -0
  36. lorebase_store/_migrations/versions/0033_vault_moved_tokens.py +30 -0
  37. lorebase_store/_migrations/versions/0034_inference_endpoint_modes.py +73 -0
  38. lorebase_store/_migrations/versions/0035_search_truncation.py +104 -0
  39. lorebase_store/_migrations/versions/0036_lineage_maps.py +325 -0
  40. lorebase_store/_migrations/versions/0037_bm25_search.py +346 -0
  41. lorebase_store/auth/__init__.py +52 -0
  42. lorebase_store/auth/api_keys.py +161 -0
  43. lorebase_store/auth/audit.py +382 -0
  44. lorebase_store/auth/checkpoints.py +185 -0
  45. lorebase_store/auth/grants.py +161 -0
  46. lorebase_store/auth/principals.py +158 -0
  47. lorebase_store/auth/scope.py +70 -0
  48. lorebase_store/auth/sessions.py +153 -0
  49. lorebase_store/auth/unit.py +53 -0
  50. lorebase_store/auth/usage.py +70 -0
  51. lorebase_store/blobs.py +73 -0
  52. lorebase_store/bm25.py +346 -0
  53. lorebase_store/bulk.py +147 -0
  54. lorebase_store/bundle.py +1830 -0
  55. lorebase_store/changefeed.py +261 -0
  56. lorebase_store/commit/__init__.py +42 -0
  57. lorebase_store/commit/changes.py +67 -0
  58. lorebase_store/commit/errors.py +60 -0
  59. lorebase_store/commit/migrations.py +335 -0
  60. lorebase_store/commit/pipeline.py +754 -0
  61. lorebase_store/commit/schema.py +123 -0
  62. lorebase_store/db.py +58 -0
  63. lorebase_store/definer.py +50 -0
  64. lorebase_store/digests.py +99 -0
  65. lorebase_store/embeddings.py +444 -0
  66. lorebase_store/endpoint_modes.py +59 -0
  67. lorebase_store/extraction/__init__.py +61 -0
  68. lorebase_store/extraction/items.py +187 -0
  69. lorebase_store/extraction/materialize.py +364 -0
  70. lorebase_store/extraction/proposal.py +65 -0
  71. lorebase_store/extraction/review.py +168 -0
  72. lorebase_store/gc.py +970 -0
  73. lorebase_store/indexes.py +1033 -0
  74. lorebase_store/jobs.py +651 -0
  75. lorebase_store/keyring.py +72 -0
  76. lorebase_store/layout.py +335 -0
  77. lorebase_store/materialize/__init__.py +103 -0
  78. lorebase_store/materialize/apply.py +436 -0
  79. lorebase_store/materialize/background.py +1184 -0
  80. lorebase_store/materialize/branch.py +268 -0
  81. lorebase_store/materialize/build.py +536 -0
  82. lorebase_store/materialize/derived.py +540 -0
  83. lorebase_store/materialize/lineage_backfill.py +502 -0
  84. lorebase_store/materialize/links.py +266 -0
  85. lorebase_store/materialize/overlay.py +292 -0
  86. lorebase_store/materialize/paths.py +315 -0
  87. lorebase_store/materialize/rebuild.py +326 -0
  88. lorebase_store/materialize/rows.py +153 -0
  89. lorebase_store/materialize/sensitivity.py +66 -0
  90. lorebase_store/materialize/snapshot.py +205 -0
  91. lorebase_store/materialize/stale.py +444 -0
  92. lorebase_store/materialize/verify.py +321 -0
  93. lorebase_store/merging/__init__.py +39 -0
  94. lorebase_store/merging/attempts.py +334 -0
  95. lorebase_store/merging/lineage.py +123 -0
  96. lorebase_store/merging/service.py +652 -0
  97. lorebase_store/migrations.py +133 -0
  98. lorebase_store/models/__init__.py +136 -0
  99. lorebase_store/models/base.py +64 -0
  100. lorebase_store/models/enums.py +132 -0
  101. lorebase_store/models/governance.py +268 -0
  102. lorebase_store/models/ingestion.py +288 -0
  103. lorebase_store/models/materialized.py +277 -0
  104. lorebase_store/models/proposals.py +195 -0
  105. lorebase_store/models/queues.py +87 -0
  106. lorebase_store/models/tracing.py +116 -0
  107. lorebase_store/models/versioning.py +222 -0
  108. lorebase_store/models/webhooks.py +96 -0
  109. lorebase_store/nodestore.py +384 -0
  110. lorebase_store/objectstore.py +162 -0
  111. lorebase_store/outbox_events.py +166 -0
  112. lorebase_store/profiling/__init__.py +58 -0
  113. lorebase_store/profiling/delimited.py +109 -0
  114. lorebase_store/profiling/postgres.py +211 -0
  115. lorebase_store/profiling/record_files.py +127 -0
  116. lorebase_store/profiling/records.py +92 -0
  117. lorebase_store/profiling/sql_databases.py +298 -0
  118. lorebase_store/ratelimit.py +129 -0
  119. lorebase_store/reads.py +1491 -0
  120. lorebase_store/replicas.py +434 -0
  121. lorebase_store/repositories/__init__.py +152 -0
  122. lorebase_store/repositories/changesets.py +59 -0
  123. lorebase_store/repositories/deferred_events.py +142 -0
  124. lorebase_store/repositories/documents.py +305 -0
  125. lorebase_store/repositories/domains.py +97 -0
  126. lorebase_store/repositories/events.py +205 -0
  127. lorebase_store/repositories/index_view.py +489 -0
  128. lorebase_store/repositories/invocations.py +68 -0
  129. lorebase_store/repositories/materializations.py +276 -0
  130. lorebase_store/repositories/materialized.py +1129 -0
  131. lorebase_store/repositories/merges.py +417 -0
  132. lorebase_store/repositories/proposal_items.py +199 -0
  133. lorebase_store/repositories/proposals.py +184 -0
  134. lorebase_store/repositories/provenance.py +77 -0
  135. lorebase_store/repositories/refs.py +452 -0
  136. lorebase_store/repositories/revisions.py +273 -0
  137. lorebase_store/repositories/scope.py +33 -0
  138. lorebase_store/repositories/source_runs.py +123 -0
  139. lorebase_store/repositories/suggestions.py +638 -0
  140. lorebase_store/runtime.py +87 -0
  141. lorebase_store/schema_cache.py +50 -0
  142. lorebase_store/settings.py +879 -0
  143. lorebase_store/storage.py +1478 -0
  144. lorebase_store/telemetry.py +142 -0
  145. lorebase_store/testing.py +108 -0
  146. lorebase_store/tracing.py +505 -0
  147. lorebase_store/traverse.py +578 -0
  148. lorebase_store/vault/__init__.py +50 -0
  149. lorebase_store/vault/keys.py +216 -0
  150. lorebase_store/vault/quotes.py +133 -0
  151. lorebase_store/vault/rotation.py +209 -0
  152. lorebase_store/vault/settings.py +152 -0
  153. lorebase_store/vault/store.py +987 -0
  154. lorebase_store/vault/transfer.py +558 -0
  155. lorebase_store/webhooks/__init__.py +24 -0
  156. lorebase_store/webhooks/delivery.py +637 -0
  157. lorebase_store/webhooks/keys.py +126 -0
  158. lorebase_store/webhooks/repository.py +142 -0
  159. lorebase_store/webhooks/settings.py +138 -0
  160. lorebase_store/webhooks/signing.py +50 -0
  161. lorebase_store-0.2.3.dist-info/METADATA +13 -0
  162. lorebase_store-0.2.3.dist-info/RECORD +163 -0
  163. lorebase_store-0.2.3.dist-info/WHEEL +4 -0
@@ -0,0 +1,268 @@
1
+ """Principals, groups, grants, API keys, web sessions, Operation invocations and audit
2
+ (design 7.4, 10, 13)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from datetime import date, datetime
7
+ from typing import Any
8
+
9
+ from sqlalchemy import (
10
+ BigInteger,
11
+ Double,
12
+ ForeignKey,
13
+ Identity,
14
+ Index,
15
+ Integer,
16
+ text,
17
+ )
18
+ from sqlalchemy.dialects.postgresql import ARRAY, CIDR, JSONB
19
+ from sqlalchemy.orm import Mapped, mapped_column
20
+
21
+ from lorebase_store.models.base import H32, Base, DomainKey, domain_fk, h32_check
22
+ from lorebase_store.models.enums import (
23
+ GRANT_SCOPE,
24
+ INVOCATION_STATUS,
25
+ PRINCIPAL_TYPE,
26
+ GrantScope,
27
+ InvocationStatus,
28
+ PrincipalType,
29
+ )
30
+
31
+
32
+ class Principal(Base):
33
+ """User, service or API key identity. Ids are prefixed (`usr_`, `svc_`)."""
34
+
35
+ __tablename__ = "principals"
36
+ __table_args__ = (
37
+ Index(
38
+ None,
39
+ "external_subject",
40
+ unique=True,
41
+ postgresql_where=text("external_subject IS NOT NULL"),
42
+ ),
43
+ )
44
+
45
+ id: Mapped[str] = mapped_column(primary_key=True)
46
+ type: Mapped[PrincipalType] = mapped_column(PRINCIPAL_TYPE)
47
+ display_name: Mapped[str]
48
+ external_subject: Mapped[str | None]
49
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
50
+
51
+
52
+ class Group(Base):
53
+ """Group of principals, optionally mapped from an identity provider claim."""
54
+
55
+ __tablename__ = "groups"
56
+ __table_args__ = (
57
+ Index(
58
+ None,
59
+ "external_group",
60
+ unique=True,
61
+ postgresql_where=text("external_group IS NOT NULL"),
62
+ ),
63
+ )
64
+
65
+ id: Mapped[str] = mapped_column(primary_key=True)
66
+ display_name: Mapped[str]
67
+ external_group: Mapped[str | None]
68
+
69
+
70
+ class GroupMember(Base):
71
+ """Membership of a principal in a group."""
72
+
73
+ __tablename__ = "group_members"
74
+ __table_args__ = (Index(None, "principal_id"),)
75
+
76
+ group_id: Mapped[str] = mapped_column(
77
+ ForeignKey("groups.id", ondelete="CASCADE"), primary_key=True
78
+ )
79
+ principal_id: Mapped[str] = mapped_column(
80
+ ForeignKey("principals.id", ondelete="CASCADE"), primary_key=True
81
+ )
82
+
83
+
84
+ class Grant(Base):
85
+ """Role of a subject (principal or group id) in a Domain, optionally scoped."""
86
+
87
+ __tablename__ = "grants"
88
+ __table_args__ = (domain_fk(), Index(None, "domain_id", "subject"), Index(None, "subject"))
89
+
90
+ id: Mapped[str] = mapped_column(primary_key=True)
91
+ domain_id: Mapped[str]
92
+ subject: Mapped[str]
93
+ role: Mapped[str]
94
+ scope_type: Mapped[GrantScope] = mapped_column(GRANT_SCOPE)
95
+ scope_ref: Mapped[str | None]
96
+ created_by: Mapped[str]
97
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
98
+
99
+
100
+ class ApiKey(Base):
101
+ """API key of a service principal; only an HMAC of the secret is stored."""
102
+
103
+ __tablename__ = "api_keys"
104
+ __table_args__ = (h32_check("hash"),)
105
+
106
+ id: Mapped[str] = mapped_column(primary_key=True)
107
+ principal_id: Mapped[str] = mapped_column(
108
+ ForeignKey("principals.id", ondelete="CASCADE"), index=True
109
+ )
110
+ prefix: Mapped[str] = mapped_column(unique=True)
111
+ hash: Mapped[H32]
112
+ scopes: Mapped[Any] = mapped_column(JSONB, server_default=text("'[]'::jsonb"))
113
+ expires_at: Mapped[datetime | None]
114
+ last_used_at: Mapped[datetime | None]
115
+ revoked_at: Mapped[datetime | None]
116
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
117
+ # Client networks the key may be used from; null: any address.
118
+ allowed_networks: Mapped[list[Any] | None] = mapped_column(ARRAY(CIDR))
119
+
120
+
121
+ class ApiKeyUsage(Base):
122
+ """Requests and context-pack tokens a key used on one UTC day (daily quotas)."""
123
+
124
+ __tablename__ = "api_key_usage"
125
+
126
+ api_key_id: Mapped[str] = mapped_column(
127
+ ForeignKey("api_keys.id", ondelete="CASCADE"), primary_key=True
128
+ )
129
+ day: Mapped[date] = mapped_column(primary_key=True)
130
+ requests: Mapped[int] = mapped_column(BigInteger, server_default=text("0"))
131
+ context_tokens: Mapped[int] = mapped_column(BigInteger, server_default=text("0"))
132
+ updated_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
133
+
134
+
135
+ class WebSession(Base):
136
+ """Signed-in browser session of a user (Keeper as backend-for-frontend, design 13.2).
137
+
138
+ Only the SHA-256 digest of the session id is stored; `tokens` holds the
139
+ provider's refresh and ID tokens encrypted by Keeper. Installation-wide like `api_keys`.
140
+ """
141
+
142
+ __tablename__ = "web_sessions"
143
+ __table_args__ = (h32_check("id_hash"),)
144
+
145
+ id_hash: Mapped[H32] = mapped_column(primary_key=True)
146
+ principal_id: Mapped[str] = mapped_column(
147
+ ForeignKey("principals.id", ondelete="CASCADE"), index=True
148
+ )
149
+ subject: Mapped[str]
150
+ external_groups: Mapped[Any] = mapped_column(JSONB, server_default=text("'[]'::jsonb"))
151
+ tokens: Mapped[bytes | None]
152
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
153
+ last_seen_at: Mapped[datetime] = mapped_column(index=True)
154
+ expires_at: Mapped[datetime] = mapped_column(index=True)
155
+ refresh_at: Mapped[datetime | None]
156
+
157
+
158
+ class OperationInvocation(Base):
159
+ """One Operation execution; idempotent per principal and key."""
160
+
161
+ __tablename__ = "operation_invocations"
162
+ __table_args__ = (
163
+ domain_fk(),
164
+ Index(
165
+ "uq_operation_invocations_idempotency",
166
+ "domain_id",
167
+ "principal",
168
+ "idempotency_key",
169
+ unique=True,
170
+ postgresql_where=text("idempotency_key IS NOT NULL"),
171
+ ),
172
+ )
173
+
174
+ domain_id: Mapped[DomainKey]
175
+ id: Mapped[str] = mapped_column(primary_key=True)
176
+ operation_id: Mapped[str]
177
+ principal: Mapped[str]
178
+ params: Mapped[dict[str, Any]]
179
+ status: Mapped[InvocationStatus] = mapped_column(INVOCATION_STATUS)
180
+ revision_id: Mapped[H32 | None]
181
+ proposal_id: Mapped[str | None]
182
+ idempotency_key: Mapped[str | None]
183
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
184
+ duration_ms: Mapped[int | None] = mapped_column(Integer)
185
+
186
+
187
+ class OperationDeferredEvent(Base):
188
+ """An `operation.<side effect>` event of an execution committed on a branch whose
189
+ Proposal is its review (agent writes): written to the outbox, and deleted here, in the
190
+ transaction of the merge that brings `revision` (the head after the execution) into the
191
+ target. Deleted with its branch when the branch is deleted unmerged."""
192
+
193
+ __tablename__ = "operation_deferred_events"
194
+ __table_args__ = (domain_fk(), Index(None, "domain_id", "branch"))
195
+
196
+ id: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True)
197
+ domain_id: Mapped[str]
198
+ branch: Mapped[str]
199
+ revision_id: Mapped[H32]
200
+ invocation_id: Mapped[str]
201
+ event_type: Mapped[str]
202
+ payload: Mapped[dict[str, Any]]
203
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
204
+
205
+
206
+ class _AuditColumns:
207
+ """Columns of a hash-chained audit table."""
208
+
209
+ seq: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True)
210
+ domain_id: Mapped[str | None]
211
+ at: Mapped[datetime] = mapped_column(server_default=text("now()"))
212
+ actor: Mapped[str]
213
+ action: Mapped[str]
214
+ target: Mapped[str | None]
215
+ details: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
216
+ prev_hash: Mapped[H32 | None]
217
+ entry_hash: Mapped[H32]
218
+
219
+
220
+ class AuditEntry(_AuditColumns, Base):
221
+ """Hash-chained audit entry: `entry_hash = sha256(prev_hash || canonical entry)`.
222
+
223
+ `domain_id` is null for installation-wide actions and has no foreign key, so audit
224
+ survives Domain deletion.
225
+ """
226
+
227
+ __tablename__ = "audit_log"
228
+ __table_args__ = (
229
+ h32_check("entry_hash"),
230
+ Index(None, "domain_id", "seq"),
231
+ Index(None, "entry_hash", unique=True),
232
+ )
233
+
234
+
235
+ class AuditReadEntry(_AuditColumns, Base):
236
+ """Hash-chained record of a read (`keeper.auth.audit.reads`), chained like `audit_log` but
237
+ in chains of its own, so sampled, best-effort read records never touch the chains of
238
+ actions."""
239
+
240
+ __tablename__ = "audit_read_log"
241
+ __table_args__ = (
242
+ h32_check("entry_hash"),
243
+ Index(None, "domain_id", "seq"),
244
+ Index(None, "entry_hash", unique=True),
245
+ )
246
+
247
+
248
+ class RateLimitBucket(Base):
249
+ """Shared token bucket of Keeper's rate limits (`keeper.limits.rate.backend: postgres`).
250
+
251
+ UNLOGGED: buckets are lost on a crash of the server and start full again. `key` is the
252
+ SHA-256 of the bucket name, so client addresses and principal ids are not stored;
253
+ `full_at` (epoch seconds) is when the bucket will be full, after which the row is
254
+ redundant and may be deleted.
255
+ """
256
+
257
+ __tablename__ = "rate_limit_buckets"
258
+ __table_args__ = (
259
+ h32_check("key"),
260
+ Index(None, "full_at"),
261
+ {"prefixes": ["UNLOGGED"]},
262
+ )
263
+
264
+ key: Mapped[H32] = mapped_column(primary_key=True)
265
+ tokens: Mapped[float] = mapped_column(Double)
266
+ updated: Mapped[float] = mapped_column(Double)
267
+ full_at: Mapped[float] = mapped_column(Double)
268
+ granted: Mapped[float] = mapped_column(Double)
@@ -0,0 +1,288 @@
1
+ """Source runs, documents, extraction, schema suggestions and the personal data vault (design
2
+ 7.4, 9, 13.5)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from datetime import datetime
7
+ from typing import Any
8
+
9
+ from sqlalchemy import (
10
+ BigInteger,
11
+ CheckConstraint,
12
+ Float,
13
+ ForeignKeyConstraint,
14
+ Identity,
15
+ Index,
16
+ Integer,
17
+ LargeBinary,
18
+ text,
19
+ )
20
+ from sqlalchemy.dialects.postgresql import JSONB
21
+ from sqlalchemy.orm import Mapped, mapped_column
22
+
23
+ from lorebase_store.models.base import H32, Base, DomainKey, domain_fk, h32_check
24
+ from lorebase_store.models.enums import RUN_STATUS, RunStatus
25
+
26
+
27
+ class SourceRun(Base):
28
+ """One synchronization run of a structured Source."""
29
+
30
+ __tablename__ = "source_runs"
31
+ __table_args__ = (domain_fk(), Index(None, "domain_id", "source_id", "started_at"))
32
+
33
+ domain_id: Mapped[DomainKey]
34
+ id: Mapped[str] = mapped_column(primary_key=True)
35
+ source_id: Mapped[str]
36
+ status: Mapped[RunStatus] = mapped_column(RUN_STATUS)
37
+ started_at: Mapped[datetime | None]
38
+ finished_at: Mapped[datetime | None]
39
+ stats: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
40
+ revision_id: Mapped[H32 | None]
41
+ proposal_id: Mapped[str | None]
42
+ error: Mapped[dict[str, Any] | None]
43
+
44
+
45
+ class Document(Base):
46
+ """Uploaded document; the bytes live in the blob store at `blob_ref`. One row per Domain
47
+ and content hash."""
48
+
49
+ __tablename__ = "documents"
50
+ __table_args__ = (
51
+ h32_check("sha256"),
52
+ domain_fk(),
53
+ Index(None, "domain_id", "sha256", unique=True),
54
+ )
55
+
56
+ domain_id: Mapped[DomainKey]
57
+ id: Mapped[str] = mapped_column(primary_key=True)
58
+ filename: Mapped[str]
59
+ media_type: Mapped[str]
60
+ sha256: Mapped[H32]
61
+ size: Mapped[int] = mapped_column(BigInteger)
62
+ blob_ref: Mapped[str]
63
+ text_hash: Mapped[H32 | None]
64
+ metadata_: Mapped[dict[str, Any]] = mapped_column(
65
+ "metadata", server_default=text("'{}'::jsonb")
66
+ )
67
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
68
+ created_by: Mapped[str]
69
+
70
+
71
+ class DocumentBlob(Base):
72
+ """Document bytes of the Postgres blob store backend, addressed by SHA-256 per Domain."""
73
+
74
+ __tablename__ = "document_blobs"
75
+ __table_args__ = (h32_check("sha256"), domain_fk())
76
+
77
+ domain_id: Mapped[DomainKey]
78
+ sha256: Mapped[H32] = mapped_column(primary_key=True)
79
+ content: Mapped[bytes] = mapped_column(LargeBinary)
80
+ size: Mapped[int] = mapped_column(BigInteger)
81
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
82
+
83
+
84
+ class DocumentChunk(Base):
85
+ """Text span of a document used for extraction and evidence offsets."""
86
+
87
+ __tablename__ = "document_chunks"
88
+ __table_args__ = (
89
+ ForeignKeyConstraint(
90
+ ["domain_id", "document_id"],
91
+ ["documents.domain_id", "documents.id"],
92
+ ondelete="CASCADE",
93
+ ),
94
+ )
95
+
96
+ domain_id: Mapped[DomainKey]
97
+ document_id: Mapped[str] = mapped_column(primary_key=True)
98
+ seq: Mapped[int] = mapped_column(Integer, primary_key=True)
99
+ start_offset: Mapped[int] = mapped_column(Integer)
100
+ end_offset: Mapped[int] = mapped_column(Integer)
101
+ text: Mapped[str]
102
+ chunk_hash: Mapped[H32]
103
+
104
+
105
+ class ExtractionJob(Base):
106
+ """Extraction over documents producing a Proposal."""
107
+
108
+ __tablename__ = "extraction_jobs"
109
+ __table_args__ = (domain_fk(), Index(None, "documents", postgresql_using="gin"))
110
+
111
+ domain_id: Mapped[DomainKey]
112
+ id: Mapped[str] = mapped_column(primary_key=True)
113
+ documents: Mapped[Any] = mapped_column(JSONB)
114
+ schema_hash: Mapped[H32]
115
+ model: Mapped[str]
116
+ prompt_version: Mapped[str]
117
+ status: Mapped[RunStatus] = mapped_column(RUN_STATUS)
118
+ budget: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
119
+ usage: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
120
+ proposal_id: Mapped[str | None]
121
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
122
+ created_by: Mapped[str | None]
123
+ #: The extraction branch (`extract/<job id>`).
124
+ branch: Mapped[str | None]
125
+ started_at: Mapped[datetime | None]
126
+ finished_at: Mapped[datetime | None]
127
+ stats: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
128
+ error: Mapped[dict[str, Any] | None]
129
+
130
+
131
+ class ExtractionCacheEntry(Base):
132
+ """Model output cached by content key; holds no Domain data beyond the key."""
133
+
134
+ __tablename__ = "extraction_cache"
135
+ __table_args__ = (h32_check("cache_key"),)
136
+
137
+ cache_key: Mapped[H32] = mapped_column(primary_key=True)
138
+ model: Mapped[str]
139
+ output: Mapped[dict[str, Any]]
140
+ usage: Mapped[dict[str, Any]]
141
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
142
+
143
+
144
+ class InferenceEndpointMode(Base):
145
+ """Structured output mode that a chat endpoint was found to need (`json_schema`: 0,
146
+ `json_mode`: 1, `prompt`: 2), so a restarted process does not retry the stronger ones.
147
+
148
+ `key` is the SHA-256 of the endpoint, model and output settings: no address is stored in
149
+ the clear (a keyed digest when `inference.mode_memory_key` is set). Holds no Domain data.
150
+ """
151
+
152
+ __tablename__ = "inference_endpoint_modes"
153
+ __table_args__ = (
154
+ h32_check("key"),
155
+ CheckConstraint("mode_index BETWEEN 0 AND 2", name="mode_index"),
156
+ )
157
+
158
+ key: Mapped[H32] = mapped_column(primary_key=True)
159
+ mode_index: Mapped[int] = mapped_column(Integer)
160
+ updated_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
161
+
162
+
163
+ class SchemaSuggestion(Base):
164
+ """A new Kind, Attribute or Relation that extraction found outside the schema, aggregated
165
+ over documents by `fingerprint` (`lorebase_core.infer.suggestions`).
166
+
167
+ `observations` maps document ids to `{"n": count, "c": summed confidence}`, so extracting a
168
+ document again replaces its share instead of adding to it; `support` is the sum of the
169
+ counts. `examples` holds evidence (`document_id`, `chunk`, `start`, `end`, `quote`) and
170
+ `samples` distinct sample values, both bounded. Accepting a suggestion opens a schema change
171
+ Proposal (`proposal_id`, `branch`); nothing applies it to a schema by itself.
172
+ """
173
+
174
+ __tablename__ = "schema_suggestions"
175
+ __table_args__ = (
176
+ domain_fk(),
177
+ Index(None, "domain_id", "fingerprint", unique=True),
178
+ Index(None, "domain_id", "status", "support"),
179
+ CheckConstraint(
180
+ "suggestion_type IN ('kind', 'attribute', 'relation')", name="suggestion_type"
181
+ ),
182
+ CheckConstraint("status IN ('pending', 'accepted', 'rejected')", name="status"),
183
+ )
184
+
185
+ domain_id: Mapped[DomainKey]
186
+ id: Mapped[str] = mapped_column(primary_key=True)
187
+ fingerprint: Mapped[str]
188
+ suggestion_type: Mapped[str]
189
+ name: Mapped[str]
190
+ #: Owner Kind of an Attribute, from Kind of a Relation: a Kind id or `+<KindName>`.
191
+ kind_ref: Mapped[str | None]
192
+ #: To Kind of a Relation.
193
+ target_ref: Mapped[str | None]
194
+ labels: Mapped[Any] = mapped_column(JSONB, server_default=text("'[]'::jsonb"))
195
+ support: Mapped[int] = mapped_column(Integer, server_default=text("0"))
196
+ confidence: Mapped[float] = mapped_column(Float, server_default=text("0"))
197
+ observations: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
198
+ examples: Mapped[Any] = mapped_column(JSONB, server_default=text("'[]'::jsonb"))
199
+ samples: Mapped[Any] = mapped_column(JSONB, server_default=text("'[]'::jsonb"))
200
+ sources: Mapped[Any] = mapped_column(JSONB, server_default=text("'[]'::jsonb"))
201
+ status: Mapped[str] = mapped_column(server_default=text("'pending'"))
202
+ decided_by: Mapped[str | None]
203
+ decided_at: Mapped[datetime | None]
204
+ proposal_id: Mapped[str | None]
205
+ branch: Mapped[str | None]
206
+ last_job_id: Mapped[str | None]
207
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
208
+ updated_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
209
+
210
+
211
+ class VaultEntry(Base):
212
+ """Encrypted personal value referenced from Record objects by its token (`id`); erasure
213
+ clears the ciphertext and the fingerprint and sets `erased_at` (`lorebase_store.vault`).
214
+
215
+ The Kind, Record key and Attribute bind a token to the value it stands for; `fingerprint`
216
+ (a keyed hash of the value without the Record key) finds the rows erasure by value erases.
217
+ `key_id` names the encryption key and `fingerprint_key` the token key and scheme of the
218
+ fingerprint (`<token_key_id>/<scheme>`, null for rows written before key rotation).
219
+ """
220
+
221
+ __tablename__ = "vault"
222
+ __table_args__ = (
223
+ domain_fk(),
224
+ Index(None, "domain_id", "kind_id", "record_key"),
225
+ Index(None, "domain_id", "kind_id", "attribute_id", "fingerprint"),
226
+ )
227
+
228
+ domain_id: Mapped[DomainKey]
229
+ id: Mapped[str] = mapped_column(primary_key=True)
230
+ kind_id: Mapped[str]
231
+ attribute_id: Mapped[str]
232
+ record_key: Mapped[str]
233
+ fingerprint: Mapped[bytes | None]
234
+ value_ciphertext: Mapped[bytes | None]
235
+ key_id: Mapped[str | None]
236
+ fingerprint_key: Mapped[str | None]
237
+ moved_to: Mapped[str | None]
238
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
239
+ erased_at: Mapped[datetime | None]
240
+
241
+
242
+ class VaultTombstone(Base):
243
+ """A value erased from the vault: the fingerprint of every value an erasure erased, of one
244
+ Record (`record_key`) or of every Record of the Kind (null), so a later write of it is
245
+ stored erased or refused (`vault.erased_values`). Holds no value."""
246
+
247
+ __tablename__ = "vault_tombstones"
248
+ __table_args__ = (
249
+ domain_fk(),
250
+ Index(None, "domain_id", "fingerprint"),
251
+ )
252
+
253
+ domain_id: Mapped[DomainKey]
254
+ seq: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True)
255
+ kind_id: Mapped[str]
256
+ attribute_id: Mapped[str]
257
+ record_key: Mapped[str | None]
258
+ fingerprint: Mapped[bytes]
259
+ fingerprint_key: Mapped[str | None]
260
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
261
+
262
+
263
+ class VaultLineage(Base):
264
+ """A Record whose vaulted values were taken over from a Record of the same Kind under
265
+ another key (a key change): erasing either erases the values of both."""
266
+
267
+ __tablename__ = "vault_lineage"
268
+ __table_args__ = (
269
+ domain_fk(),
270
+ Index(None, "domain_id", "kind_id", "previous_key"),
271
+ )
272
+
273
+ domain_id: Mapped[DomainKey]
274
+ kind_id: Mapped[str] = mapped_column(primary_key=True)
275
+ record_key: Mapped[str] = mapped_column(primary_key=True)
276
+ previous_key: Mapped[str] = mapped_column(primary_key=True)
277
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
278
+
279
+
280
+ class InstallationSecret(Base):
281
+ """Random secrets of the installation made by the migrations, such as the key of the keyed
282
+ digests when no `vault.digest_key` is configured (`lorebase_store.digests`)."""
283
+
284
+ __tablename__ = "installation_secrets"
285
+
286
+ name: Mapped[str] = mapped_column(primary_key=True)
287
+ secret: Mapped[str]
288
+ created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))