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.
- lorebase_store/__init__.py +1 -0
- lorebase_store/_migrations/env.py +106 -0
- lorebase_store/_migrations/script.py.mako +27 -0
- lorebase_store/_migrations/versions/0001_initial_schema.py +1071 -0
- lorebase_store/_migrations/versions/0002_merge_attempt_details.py +74 -0
- lorebase_store/_migrations/versions/0003_webhooks.py +127 -0
- lorebase_store/_migrations/versions/0004_defer_revision_references.py +38 -0
- lorebase_store/_migrations/versions/0005_security_definer_reads.py +396 -0
- lorebase_store/_migrations/versions/0006_api_key_usage.py +48 -0
- lorebase_store/_migrations/versions/0007_document_extraction.py +154 -0
- lorebase_store/_migrations/versions/0008_definer_argument_checks.py +178 -0
- lorebase_store/_migrations/versions/0009_api_key_networks.py +33 -0
- lorebase_store/_migrations/versions/0010_overlay_branches.py +582 -0
- lorebase_store/_migrations/versions/0011_branch_scoped_search_index.py +229 -0
- lorebase_store/_migrations/versions/0012_overlay_neighborhood_lookups.py +185 -0
- lorebase_store/_migrations/versions/0013_web_sessions.py +72 -0
- lorebase_store/_migrations/versions/0014_embedding_jobs.py +70 -0
- lorebase_store/_migrations/versions/0015_append_only_audit_log.py +83 -0
- lorebase_store/_migrations/versions/0016_sealed_webhook_keys.py +55 -0
- lorebase_store/_migrations/versions/0017_background_branch_copies.py +77 -0
- lorebase_store/_migrations/versions/0018_search_lexicon.py +477 -0
- lorebase_store/_migrations/versions/0019_vault_bindings.py +56 -0
- lorebase_store/_migrations/versions/0020_outbox_consumers.py +150 -0
- lorebase_store/_migrations/versions/0021_schema_suggestions.py +114 -0
- lorebase_store/_migrations/versions/0022_read_audit_and_shared_rate_limits.py +133 -0
- lorebase_store/_migrations/versions/0023_agent_trace.py +96 -0
- lorebase_store/_migrations/versions/0024_embedding_template_keys.py +205 -0
- lorebase_store/_migrations/versions/0025_snapshot_refs.py +35 -0
- lorebase_store/_migrations/versions/0026_plain_reverse_link_index.py +112 -0
- lorebase_store/_migrations/versions/0027_vault_rotation_and_tombstones.py +145 -0
- lorebase_store/_migrations/versions/0028_operation_deferred_events.py +71 -0
- lorebase_store/_migrations/versions/0029_agent_trace_sessions.py +108 -0
- lorebase_store/_migrations/versions/0030_path_fanout.py +31 -0
- lorebase_store/_migrations/versions/0031_branch_builds.py +91 -0
- lorebase_store/_migrations/versions/0032_lineage_revisions.py +35 -0
- lorebase_store/_migrations/versions/0033_vault_moved_tokens.py +30 -0
- lorebase_store/_migrations/versions/0034_inference_endpoint_modes.py +73 -0
- lorebase_store/_migrations/versions/0035_search_truncation.py +104 -0
- lorebase_store/_migrations/versions/0036_lineage_maps.py +325 -0
- lorebase_store/_migrations/versions/0037_bm25_search.py +346 -0
- lorebase_store/auth/__init__.py +52 -0
- lorebase_store/auth/api_keys.py +161 -0
- lorebase_store/auth/audit.py +382 -0
- lorebase_store/auth/checkpoints.py +185 -0
- lorebase_store/auth/grants.py +161 -0
- lorebase_store/auth/principals.py +158 -0
- lorebase_store/auth/scope.py +70 -0
- lorebase_store/auth/sessions.py +153 -0
- lorebase_store/auth/unit.py +53 -0
- lorebase_store/auth/usage.py +70 -0
- lorebase_store/blobs.py +73 -0
- lorebase_store/bm25.py +346 -0
- lorebase_store/bulk.py +147 -0
- lorebase_store/bundle.py +1830 -0
- lorebase_store/changefeed.py +261 -0
- lorebase_store/commit/__init__.py +42 -0
- lorebase_store/commit/changes.py +67 -0
- lorebase_store/commit/errors.py +60 -0
- lorebase_store/commit/migrations.py +335 -0
- lorebase_store/commit/pipeline.py +754 -0
- lorebase_store/commit/schema.py +123 -0
- lorebase_store/db.py +58 -0
- lorebase_store/definer.py +50 -0
- lorebase_store/digests.py +99 -0
- lorebase_store/embeddings.py +444 -0
- lorebase_store/endpoint_modes.py +59 -0
- lorebase_store/extraction/__init__.py +61 -0
- lorebase_store/extraction/items.py +187 -0
- lorebase_store/extraction/materialize.py +364 -0
- lorebase_store/extraction/proposal.py +65 -0
- lorebase_store/extraction/review.py +168 -0
- lorebase_store/gc.py +970 -0
- lorebase_store/indexes.py +1033 -0
- lorebase_store/jobs.py +651 -0
- lorebase_store/keyring.py +72 -0
- lorebase_store/layout.py +335 -0
- lorebase_store/materialize/__init__.py +103 -0
- lorebase_store/materialize/apply.py +436 -0
- lorebase_store/materialize/background.py +1184 -0
- lorebase_store/materialize/branch.py +268 -0
- lorebase_store/materialize/build.py +536 -0
- lorebase_store/materialize/derived.py +540 -0
- lorebase_store/materialize/lineage_backfill.py +502 -0
- lorebase_store/materialize/links.py +266 -0
- lorebase_store/materialize/overlay.py +292 -0
- lorebase_store/materialize/paths.py +315 -0
- lorebase_store/materialize/rebuild.py +326 -0
- lorebase_store/materialize/rows.py +153 -0
- lorebase_store/materialize/sensitivity.py +66 -0
- lorebase_store/materialize/snapshot.py +205 -0
- lorebase_store/materialize/stale.py +444 -0
- lorebase_store/materialize/verify.py +321 -0
- lorebase_store/merging/__init__.py +39 -0
- lorebase_store/merging/attempts.py +334 -0
- lorebase_store/merging/lineage.py +123 -0
- lorebase_store/merging/service.py +652 -0
- lorebase_store/migrations.py +133 -0
- lorebase_store/models/__init__.py +136 -0
- lorebase_store/models/base.py +64 -0
- lorebase_store/models/enums.py +132 -0
- lorebase_store/models/governance.py +268 -0
- lorebase_store/models/ingestion.py +288 -0
- lorebase_store/models/materialized.py +277 -0
- lorebase_store/models/proposals.py +195 -0
- lorebase_store/models/queues.py +87 -0
- lorebase_store/models/tracing.py +116 -0
- lorebase_store/models/versioning.py +222 -0
- lorebase_store/models/webhooks.py +96 -0
- lorebase_store/nodestore.py +384 -0
- lorebase_store/objectstore.py +162 -0
- lorebase_store/outbox_events.py +166 -0
- lorebase_store/profiling/__init__.py +58 -0
- lorebase_store/profiling/delimited.py +109 -0
- lorebase_store/profiling/postgres.py +211 -0
- lorebase_store/profiling/record_files.py +127 -0
- lorebase_store/profiling/records.py +92 -0
- lorebase_store/profiling/sql_databases.py +298 -0
- lorebase_store/ratelimit.py +129 -0
- lorebase_store/reads.py +1491 -0
- lorebase_store/replicas.py +434 -0
- lorebase_store/repositories/__init__.py +152 -0
- lorebase_store/repositories/changesets.py +59 -0
- lorebase_store/repositories/deferred_events.py +142 -0
- lorebase_store/repositories/documents.py +305 -0
- lorebase_store/repositories/domains.py +97 -0
- lorebase_store/repositories/events.py +205 -0
- lorebase_store/repositories/index_view.py +489 -0
- lorebase_store/repositories/invocations.py +68 -0
- lorebase_store/repositories/materializations.py +276 -0
- lorebase_store/repositories/materialized.py +1129 -0
- lorebase_store/repositories/merges.py +417 -0
- lorebase_store/repositories/proposal_items.py +199 -0
- lorebase_store/repositories/proposals.py +184 -0
- lorebase_store/repositories/provenance.py +77 -0
- lorebase_store/repositories/refs.py +452 -0
- lorebase_store/repositories/revisions.py +273 -0
- lorebase_store/repositories/scope.py +33 -0
- lorebase_store/repositories/source_runs.py +123 -0
- lorebase_store/repositories/suggestions.py +638 -0
- lorebase_store/runtime.py +87 -0
- lorebase_store/schema_cache.py +50 -0
- lorebase_store/settings.py +879 -0
- lorebase_store/storage.py +1478 -0
- lorebase_store/telemetry.py +142 -0
- lorebase_store/testing.py +108 -0
- lorebase_store/tracing.py +505 -0
- lorebase_store/traverse.py +578 -0
- lorebase_store/vault/__init__.py +50 -0
- lorebase_store/vault/keys.py +216 -0
- lorebase_store/vault/quotes.py +133 -0
- lorebase_store/vault/rotation.py +209 -0
- lorebase_store/vault/settings.py +152 -0
- lorebase_store/vault/store.py +987 -0
- lorebase_store/vault/transfer.py +558 -0
- lorebase_store/webhooks/__init__.py +24 -0
- lorebase_store/webhooks/delivery.py +637 -0
- lorebase_store/webhooks/keys.py +126 -0
- lorebase_store/webhooks/repository.py +142 -0
- lorebase_store/webhooks/settings.py +138 -0
- lorebase_store/webhooks/signing.py +50 -0
- lorebase_store-0.2.3.dist-info/METADATA +13 -0
- lorebase_store-0.2.3.dist-info/RECORD +163 -0
- lorebase_store-0.2.3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""Materialized heads (design 7.2): compiled schema, Records, Links, history, embeddings.
|
|
2
|
+
|
|
3
|
+
Per-Attribute expression indexes on `mat_records` and per-dimension HNSW indexes on
|
|
4
|
+
`embeddings` depend on configuration and schema; they are created outside this metadata.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from pgvector.sqlalchemy import VECTOR
|
|
13
|
+
from sqlalchemy import BigInteger, CheckConstraint, ForeignKeyConstraint, Index, Integer, text
|
|
14
|
+
from sqlalchemy.dialects.postgresql import TSVECTOR
|
|
15
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
16
|
+
|
|
17
|
+
from lorebase_store.models.base import H32, HASH_PARTITIONED, Base, DomainKey, domain_fk
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _branch_fk() -> ForeignKeyConstraint:
|
|
21
|
+
"""Materialized rows belong to a ref and go when the branch is deleted."""
|
|
22
|
+
return ForeignKeyConstraint(
|
|
23
|
+
["domain_id", "branch"], ["refs.domain_id", "refs.name"], ondelete="CASCADE"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class MatSchema(Base):
|
|
28
|
+
"""Compiled schema at a branch head.
|
|
29
|
+
|
|
30
|
+
`path_fanout` is the `store.traverse.path_fanout` the branch's materialized path
|
|
31
|
+
Derivations were computed with (`None` before any was): a different setting rewrites
|
|
32
|
+
them (`lorebase_store.materialize.paths`).
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
__tablename__ = "mat_schema"
|
|
36
|
+
__table_args__ = (_branch_fk(),)
|
|
37
|
+
|
|
38
|
+
domain_id: Mapped[DomainKey]
|
|
39
|
+
branch: Mapped[str] = mapped_column(primary_key=True)
|
|
40
|
+
schema_hash: Mapped[H32]
|
|
41
|
+
compiled: Mapped[dict[str, Any]]
|
|
42
|
+
path_fanout: Mapped[int | None] = mapped_column(Integer)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MatRecord(Base):
|
|
46
|
+
"""One Record at a branch head; `values` is keyed by Attribute id.
|
|
47
|
+
|
|
48
|
+
`revision_generation` is the generation of the Revision that last wrote the row and
|
|
49
|
+
`deleted` marks a tombstone, which only overlay branches hold (store schema notes).
|
|
50
|
+
`lineage_revisions` maps each Attribute of `lineage` to the Revision (hex) of that change,
|
|
51
|
+
and `$created` to the Revision that last created the Record (revision 0032); it is null
|
|
52
|
+
on rows written before that revision, whose readers fall back to the branch history.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
__tablename__ = "mat_records"
|
|
56
|
+
__table_args__ = (
|
|
57
|
+
_branch_fk(),
|
|
58
|
+
# btree_gin (revision 0011): a search's Domain and branch are index conditions of the
|
|
59
|
+
# search vector index.
|
|
60
|
+
Index(
|
|
61
|
+
"ix_mat_records_branch_search",
|
|
62
|
+
"domain_id",
|
|
63
|
+
"branch",
|
|
64
|
+
"search",
|
|
65
|
+
postgresql_using="gin",
|
|
66
|
+
),
|
|
67
|
+
Index(
|
|
68
|
+
"ix_mat_records_values",
|
|
69
|
+
"values",
|
|
70
|
+
postgresql_using="gin",
|
|
71
|
+
postgresql_ops={"values": "jsonb_path_ops"},
|
|
72
|
+
),
|
|
73
|
+
HASH_PARTITIONED,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
domain_id: Mapped[DomainKey]
|
|
77
|
+
branch: Mapped[str] = mapped_column(primary_key=True)
|
|
78
|
+
kind_id: Mapped[str] = mapped_column(primary_key=True)
|
|
79
|
+
key: Mapped[str] = mapped_column(primary_key=True)
|
|
80
|
+
values: Mapped[dict[str, Any]]
|
|
81
|
+
derived: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
|
|
82
|
+
title: Mapped[str | None]
|
|
83
|
+
search: Mapped[str | None] = mapped_column(TSVECTOR)
|
|
84
|
+
object_hash: Mapped[H32]
|
|
85
|
+
lineage: Mapped[dict[str, Any]]
|
|
86
|
+
updated_revision: Mapped[H32]
|
|
87
|
+
updated_at: Mapped[datetime]
|
|
88
|
+
revision_generation: Mapped[int] = mapped_column(BigInteger, server_default=text("0"))
|
|
89
|
+
deleted: Mapped[bool] = mapped_column(server_default=text("false"))
|
|
90
|
+
lineage_revisions: Mapped[dict[str, Any] | None]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class MatLink(Base):
|
|
94
|
+
"""One Link at a branch head, indexed in both directions."""
|
|
95
|
+
|
|
96
|
+
__tablename__ = "mat_links"
|
|
97
|
+
__table_args__ = (
|
|
98
|
+
_branch_fk(),
|
|
99
|
+
Index("ix_mat_links_reverse", "domain_id", "branch", "relation_id", "to_key"),
|
|
100
|
+
HASH_PARTITIONED,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
domain_id: Mapped[DomainKey]
|
|
104
|
+
branch: Mapped[str] = mapped_column(primary_key=True)
|
|
105
|
+
relation_id: Mapped[str] = mapped_column(primary_key=True)
|
|
106
|
+
from_key: Mapped[str] = mapped_column(primary_key=True)
|
|
107
|
+
to_key: Mapped[str] = mapped_column(primary_key=True)
|
|
108
|
+
values: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
|
|
109
|
+
object_hash: Mapped[H32]
|
|
110
|
+
lineage: Mapped[dict[str, Any]]
|
|
111
|
+
revision_generation: Mapped[int] = mapped_column(BigInteger, server_default=text("0"))
|
|
112
|
+
deleted: Mapped[bool] = mapped_column(server_default=text("false"))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class MatLineageMap(Base):
|
|
116
|
+
"""A lineage map shared by the rows that name it (revision 0036).
|
|
117
|
+
|
|
118
|
+
With shared lineage maps (`store.storage.lineage_maps`), the map columns of `mat_records`
|
|
119
|
+
(`lineage`, `lineage_revisions`) and `mat_links` (`lineage`) hold `{"$map": "<hex id>"}`
|
|
120
|
+
and the map is stored here once per Domain; `id` is the first 16 bytes of the SHA-256 of
|
|
121
|
+
its canonical text. Readers resolve the columns with `lb_lineage(domain_id, column)`.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
__tablename__ = "mat_lineage_maps"
|
|
125
|
+
__table_args__ = (
|
|
126
|
+
CheckConstraint("octet_length(id) = 16", name="id_h16"),
|
|
127
|
+
domain_fk(),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
domain_id: Mapped[DomainKey]
|
|
131
|
+
id: Mapped[bytes] = mapped_column(primary_key=True)
|
|
132
|
+
map: Mapped[dict[str, Any]]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class StorageTask(Base):
|
|
136
|
+
"""Progress of a resumable step of `storage apply` (`lorebase_store.storage`, 0036)."""
|
|
137
|
+
|
|
138
|
+
__tablename__ = "storage_tasks"
|
|
139
|
+
|
|
140
|
+
name: Mapped[str] = mapped_column(primary_key=True)
|
|
141
|
+
state: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
|
|
142
|
+
updated_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class MatLexicon(Base):
|
|
146
|
+
"""Per Kind, the words of a branch's own search vectors and how many of its rows hold
|
|
147
|
+
each (revision 0018): typo-tolerant search matches query words against them by trigrams.
|
|
148
|
+
|
|
149
|
+
Kept by triggers on `mat_records`; words with a digit or longer than 64 characters are
|
|
150
|
+
left out. An overlay holds the words of its own rows, and its readers add its base's.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
__tablename__ = "mat_lexicon"
|
|
154
|
+
__table_args__ = (
|
|
155
|
+
_branch_fk(),
|
|
156
|
+
Index(
|
|
157
|
+
"ix_mat_lexicon_branch_word",
|
|
158
|
+
"domain_id",
|
|
159
|
+
"branch",
|
|
160
|
+
"word",
|
|
161
|
+
postgresql_using="gin",
|
|
162
|
+
postgresql_ops={"word": "gin_trgm_ops"},
|
|
163
|
+
),
|
|
164
|
+
Index("ix_mat_lexicon_spent", "domain_id", "branch", postgresql_where=text("records <= 0")),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
domain_id: Mapped[DomainKey]
|
|
168
|
+
branch: Mapped[str] = mapped_column(primary_key=True)
|
|
169
|
+
kind_id: Mapped[str] = mapped_column(primary_key=True)
|
|
170
|
+
word: Mapped[str] = mapped_column(primary_key=True)
|
|
171
|
+
records: Mapped[int] = mapped_column(BigInteger)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class MatHistory(Base):
|
|
175
|
+
"""Per branch, per element change log along the first-parent chain."""
|
|
176
|
+
|
|
177
|
+
__tablename__ = "mat_history"
|
|
178
|
+
__table_args__ = (_branch_fk(),)
|
|
179
|
+
|
|
180
|
+
domain_id: Mapped[DomainKey]
|
|
181
|
+
branch: Mapped[str] = mapped_column(primary_key=True)
|
|
182
|
+
partition: Mapped[str] = mapped_column(primary_key=True)
|
|
183
|
+
element_key: Mapped[str] = mapped_column(primary_key=True)
|
|
184
|
+
revision_id: Mapped[H32] = mapped_column(primary_key=True)
|
|
185
|
+
at: Mapped[datetime]
|
|
186
|
+
old_hash: Mapped[H32 | None]
|
|
187
|
+
new_hash: Mapped[H32 | None]
|
|
188
|
+
provenance: Mapped[dict[str, Any] | None]
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class Embedding(Base):
|
|
192
|
+
"""Vector of an object for one model and embedding template; shared by all branches whose
|
|
193
|
+
template of the object's Kind has that fingerprint.
|
|
194
|
+
|
|
195
|
+
The column is dimensionless so models of different sizes coexist; `dims` must match the
|
|
196
|
+
vector and selects the per-dimension HNSW index. `text_hash` is the SHA-256 of the text
|
|
197
|
+
that was embedded (revision 0014): an object whose text did not change keeps its vector,
|
|
198
|
+
and a new object with the text of another reuses that one's vector. `fingerprint` is the
|
|
199
|
+
template fingerprint the text was rendered with (revision 0024); empty for a vector whose
|
|
200
|
+
template could not be told when the column was added.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
__tablename__ = "embeddings"
|
|
204
|
+
__table_args__ = (
|
|
205
|
+
CheckConstraint("vector_dims(embedding) = dims", name="dims_match"),
|
|
206
|
+
domain_fk(),
|
|
207
|
+
Index("ix_embeddings_domain_id_model_text_hash", "domain_id", "model", "text_hash"),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
domain_id: Mapped[DomainKey]
|
|
211
|
+
object_hash: Mapped[H32] = mapped_column(primary_key=True)
|
|
212
|
+
model: Mapped[str] = mapped_column(primary_key=True)
|
|
213
|
+
fingerprint: Mapped[str] = mapped_column(primary_key=True)
|
|
214
|
+
dims: Mapped[int] = mapped_column(Integer)
|
|
215
|
+
embedding: Mapped[list[float]] = mapped_column(VECTOR())
|
|
216
|
+
text_hash: Mapped[H32 | None]
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class EmbeddingCursor(Base):
|
|
220
|
+
"""How far the embedding job got on a branch for one model (revision 0014).
|
|
221
|
+
|
|
222
|
+
`head` is the branch head the last complete pass read and `fingerprints` the embedding
|
|
223
|
+
template fingerprint of each embedded Kind at that head; a pass is skipped while both are
|
|
224
|
+
unchanged, and Kinds whose fingerprint changed have every text rendered again.
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
__tablename__ = "embedding_cursors"
|
|
228
|
+
__table_args__ = (_branch_fk(),)
|
|
229
|
+
|
|
230
|
+
domain_id: Mapped[DomainKey]
|
|
231
|
+
branch: Mapped[str] = mapped_column(primary_key=True)
|
|
232
|
+
model: Mapped[str] = mapped_column(primary_key=True)
|
|
233
|
+
head: Mapped[H32]
|
|
234
|
+
fingerprints: Mapped[dict[str, Any]]
|
|
235
|
+
updated_at: Mapped[datetime]
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class BranchMaterialization(Base):
|
|
239
|
+
"""Progress of turning a branch into a full copy in batches (revisions 0017, 0031).
|
|
240
|
+
|
|
241
|
+
One row per branch whose rows a `store.materialize` job writes: a copy branch created
|
|
242
|
+
as an overlay, an overlay promoted in the background (`strategy` `base`: copies the base
|
|
243
|
+
rows), or a branch built from the tree of its head (`tree`). `phase` and `cursor` say
|
|
244
|
+
where the next batch starts; `base_branch` and `base_generation` are the fork point a
|
|
245
|
+
`base` copy reads (for `tree`, the source and the head's generation). `failures` counts
|
|
246
|
+
failed jobs; `reconcile` holds schema changes made during the copy. Rows go with their
|
|
247
|
+
branch.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
__tablename__ = "branch_materializations"
|
|
251
|
+
__table_args__ = (
|
|
252
|
+
_branch_fk(),
|
|
253
|
+
CheckConstraint("status IN ('pending', 'running', 'done', 'failed')", name="status"),
|
|
254
|
+
CheckConstraint("strategy IN ('base', 'tree')", name="strategy"),
|
|
255
|
+
CheckConstraint(
|
|
256
|
+
"phase IN ('records', 'links', 'history', 'clear', 'tree_history', 'tree_rows', "
|
|
257
|
+
"'switch', 'prune', 'derived', 'paths', 'search', 'done')",
|
|
258
|
+
name="phase",
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
domain_id: Mapped[DomainKey]
|
|
263
|
+
branch: Mapped[str] = mapped_column(primary_key=True)
|
|
264
|
+
status: Mapped[str]
|
|
265
|
+
phase: Mapped[str]
|
|
266
|
+
base_branch: Mapped[str]
|
|
267
|
+
base_generation: Mapped[int] = mapped_column(BigInteger)
|
|
268
|
+
cursor: Mapped[dict[str, Any] | None]
|
|
269
|
+
rows_done: Mapped[int] = mapped_column(BigInteger, server_default=text("0"))
|
|
270
|
+
rows_total: Mapped[int | None] = mapped_column(BigInteger)
|
|
271
|
+
error: Mapped[str | None]
|
|
272
|
+
strategy: Mapped[str] = mapped_column(server_default=text("'base'"))
|
|
273
|
+
failures: Mapped[int] = mapped_column(server_default=text("0"))
|
|
274
|
+
reconcile: Mapped[dict[str, Any] | None]
|
|
275
|
+
created_at: Mapped[datetime]
|
|
276
|
+
updated_at: Mapped[datetime]
|
|
277
|
+
finished_at: Mapped[datetime | None]
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Proposals and merges (design 7.3)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import (
|
|
9
|
+
REAL,
|
|
10
|
+
CheckConstraint,
|
|
11
|
+
ForeignKeyConstraint,
|
|
12
|
+
Index,
|
|
13
|
+
Integer,
|
|
14
|
+
SmallInteger,
|
|
15
|
+
UniqueConstraint,
|
|
16
|
+
text,
|
|
17
|
+
)
|
|
18
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
19
|
+
|
|
20
|
+
from lorebase_store.models.base import H32, Base, DomainKey, domain_fk
|
|
21
|
+
from lorebase_store.models.enums import (
|
|
22
|
+
ITEM_DECISION,
|
|
23
|
+
MERGE_ATTEMPT_STATUS,
|
|
24
|
+
PROPOSAL_ORIGIN,
|
|
25
|
+
PROPOSAL_STATUS,
|
|
26
|
+
REVIEW_VERDICT,
|
|
27
|
+
ItemDecision,
|
|
28
|
+
MergeAttemptStatus,
|
|
29
|
+
ProposalOrigin,
|
|
30
|
+
ProposalStatus,
|
|
31
|
+
ReviewVerdict,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _proposal_fk() -> ForeignKeyConstraint:
|
|
36
|
+
return ForeignKeyConstraint(
|
|
37
|
+
["domain_id", "proposal_id"],
|
|
38
|
+
["proposals.domain_id", "proposals.id"],
|
|
39
|
+
ondelete="CASCADE",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Proposal(Base):
|
|
44
|
+
"""Request to merge a source ref into a target ref."""
|
|
45
|
+
|
|
46
|
+
__tablename__ = "proposals"
|
|
47
|
+
__table_args__ = (
|
|
48
|
+
domain_fk(),
|
|
49
|
+
Index(None, "domain_id", "target_ref", "status"),
|
|
50
|
+
Index(
|
|
51
|
+
"uq_proposals_idempotency",
|
|
52
|
+
"domain_id",
|
|
53
|
+
"author",
|
|
54
|
+
"idempotency_key",
|
|
55
|
+
unique=True,
|
|
56
|
+
postgresql_where=text("idempotency_key IS NOT NULL"),
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
domain_id: Mapped[DomainKey]
|
|
61
|
+
id: Mapped[str] = mapped_column(primary_key=True)
|
|
62
|
+
title: Mapped[str]
|
|
63
|
+
description: Mapped[str | None]
|
|
64
|
+
source_ref: Mapped[str]
|
|
65
|
+
target_ref: Mapped[str]
|
|
66
|
+
origin: Mapped[ProposalOrigin] = mapped_column(PROPOSAL_ORIGIN)
|
|
67
|
+
status: Mapped[ProposalStatus] = mapped_column(PROPOSAL_STATUS)
|
|
68
|
+
author: Mapped[str]
|
|
69
|
+
created_at: Mapped[datetime]
|
|
70
|
+
merged_revision: Mapped[H32 | None]
|
|
71
|
+
checks: Mapped[dict[str, Any] | None]
|
|
72
|
+
policies: Mapped[dict[str, Any] | None]
|
|
73
|
+
idempotency_key: Mapped[str | None]
|
|
74
|
+
updated_at: Mapped[datetime | None]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ProposalReview(Base):
|
|
78
|
+
"""One review of a Proposal; a reviewer may review several times."""
|
|
79
|
+
|
|
80
|
+
__tablename__ = "proposal_reviews"
|
|
81
|
+
__table_args__ = (_proposal_fk(), Index(None, "domain_id", "proposal_id"))
|
|
82
|
+
|
|
83
|
+
domain_id: Mapped[DomainKey]
|
|
84
|
+
id: Mapped[str] = mapped_column(primary_key=True)
|
|
85
|
+
proposal_id: Mapped[str]
|
|
86
|
+
reviewer: Mapped[str]
|
|
87
|
+
verdict: Mapped[ReviewVerdict] = mapped_column(REVIEW_VERDICT)
|
|
88
|
+
comment: Mapped[str | None]
|
|
89
|
+
at: Mapped[datetime]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ProposalItem(Base):
|
|
93
|
+
"""One extraction Proposal item: a proposed Record, Attribute value or Link with its
|
|
94
|
+
evidence, confidence and decision."""
|
|
95
|
+
|
|
96
|
+
__tablename__ = "proposal_items"
|
|
97
|
+
__table_args__ = (
|
|
98
|
+
_proposal_fk(),
|
|
99
|
+
CheckConstraint("item_type IN ('record', 'value', 'link')", name="item_type"),
|
|
100
|
+
Index(None, "domain_id", "proposal_id", "seq"),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
domain_id: Mapped[DomainKey]
|
|
104
|
+
proposal_id: Mapped[str] = mapped_column(primary_key=True)
|
|
105
|
+
item_id: Mapped[str] = mapped_column(primary_key=True)
|
|
106
|
+
partition: Mapped[str]
|
|
107
|
+
element_key: Mapped[str]
|
|
108
|
+
attribute_id: Mapped[str | None]
|
|
109
|
+
evidence: Mapped[dict[str, Any] | None]
|
|
110
|
+
confidence: Mapped[float | None] = mapped_column(REAL)
|
|
111
|
+
decision: Mapped[ItemDecision] = mapped_column(ITEM_DECISION)
|
|
112
|
+
decided_by: Mapped[str | None]
|
|
113
|
+
decided_at: Mapped[datetime | None]
|
|
114
|
+
#: `record` (new Record), `value` (Attribute value of an existing Record) or `link`.
|
|
115
|
+
item_type: Mapped[str] = mapped_column(server_default=text("'record'"))
|
|
116
|
+
seq: Mapped[int] = mapped_column(Integer, server_default=text("0"))
|
|
117
|
+
job_id: Mapped[str | None]
|
|
118
|
+
#: What the item proposes: Kind and key with values, or Relation and endpoints.
|
|
119
|
+
subject: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
|
|
120
|
+
#: Entity resolution outcome: status, method, candidates.
|
|
121
|
+
match: Mapped[dict[str, Any] | None]
|
|
122
|
+
tier: Mapped[int | None] = mapped_column(SmallInteger)
|
|
123
|
+
model: Mapped[str | None]
|
|
124
|
+
#: Reviewer edits applied over `subject` values when the item is accepted.
|
|
125
|
+
edits: Mapped[dict[str, Any] | None]
|
|
126
|
+
created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class MergeAttempt(Base):
|
|
130
|
+
"""Stored three-way merge with Conflicts, attached to a Proposal."""
|
|
131
|
+
|
|
132
|
+
__tablename__ = "merge_attempts"
|
|
133
|
+
__table_args__ = (_proposal_fk(), Index(None, "domain_id", "proposal_id"))
|
|
134
|
+
|
|
135
|
+
domain_id: Mapped[DomainKey]
|
|
136
|
+
id: Mapped[str] = mapped_column(primary_key=True)
|
|
137
|
+
proposal_id: Mapped[str]
|
|
138
|
+
#: Merge base; null when the heads share no ancestor or the base is virtual (AC-13).
|
|
139
|
+
base: Mapped[H32 | None]
|
|
140
|
+
ours: Mapped[H32]
|
|
141
|
+
theirs: Mapped[H32]
|
|
142
|
+
status: Mapped[MergeAttemptStatus] = mapped_column(MERGE_ATTEMPT_STATUS)
|
|
143
|
+
created_at: Mapped[datetime]
|
|
144
|
+
virtual_base: Mapped[bool] = mapped_column(server_default=text("false"))
|
|
145
|
+
#: `conflicts`, `merged`, `fast_forward` or `up_to_date`.
|
|
146
|
+
result: Mapped[str | None]
|
|
147
|
+
policy: Mapped[dict[str, Any] | None]
|
|
148
|
+
created_by: Mapped[str | None]
|
|
149
|
+
#: Automatic resolutions, resolutions that matched no Conflict, Conflict counts.
|
|
150
|
+
outcome: Mapped[dict[str, Any] | None]
|
|
151
|
+
#: The Revision the target moved to when the attempt was committed.
|
|
152
|
+
revision: Mapped[H32 | None]
|
|
153
|
+
idempotency_key: Mapped[str | None]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class Conflict(Base):
|
|
157
|
+
"""One Conflict of a merge attempt, identified by its fingerprint (design 6.10)."""
|
|
158
|
+
|
|
159
|
+
__tablename__ = "conflicts"
|
|
160
|
+
__table_args__ = (
|
|
161
|
+
ForeignKeyConstraint(
|
|
162
|
+
["domain_id", "attempt_id"],
|
|
163
|
+
["merge_attempts.domain_id", "merge_attempts.id"],
|
|
164
|
+
ondelete="CASCADE",
|
|
165
|
+
),
|
|
166
|
+
UniqueConstraint("domain_id", "attempt_id", "fingerprint"),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
domain_id: Mapped[DomainKey]
|
|
170
|
+
attempt_id: Mapped[str] = mapped_column(primary_key=True)
|
|
171
|
+
id: Mapped[str] = mapped_column(primary_key=True)
|
|
172
|
+
code: Mapped[str]
|
|
173
|
+
fingerprint: Mapped[H32]
|
|
174
|
+
partition: Mapped[str]
|
|
175
|
+
element_key: Mapped[str | None]
|
|
176
|
+
attribute_id: Mapped[str | None]
|
|
177
|
+
payload: Mapped[dict[str, Any]]
|
|
178
|
+
member_count: Mapped[int] = mapped_column(Integer)
|
|
179
|
+
resolution: Mapped[dict[str, Any] | None]
|
|
180
|
+
resolved_by: Mapped[str | None]
|
|
181
|
+
resolved_at: Mapped[datetime | None]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class ConflictResolution(Base):
|
|
185
|
+
"""Resolution stored by fingerprint so it survives merge attempt recomputation."""
|
|
186
|
+
|
|
187
|
+
__tablename__ = "conflict_resolutions"
|
|
188
|
+
__table_args__ = (_proposal_fk(),)
|
|
189
|
+
|
|
190
|
+
domain_id: Mapped[DomainKey]
|
|
191
|
+
proposal_id: Mapped[str] = mapped_column(primary_key=True)
|
|
192
|
+
fingerprint: Mapped[H32] = mapped_column(primary_key=True)
|
|
193
|
+
resolution: Mapped[dict[str, Any]]
|
|
194
|
+
resolved_by: Mapped[str]
|
|
195
|
+
at: Mapped[datetime]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Outbox events, their consumers and the job queue (design 7.4, 10.5).
|
|
2
|
+
|
|
3
|
+
Neither events nor jobs reference `domains`: events and jobs about a Domain outlive its
|
|
4
|
+
deletion.
|
|
5
|
+
|
|
6
|
+
Each consumer of the outbox keeps its own delivery state: a trigger on `outbox` (revision 0020)
|
|
7
|
+
adds one `outbox_pending` row per registered consumer in the transaction that writes the event,
|
|
8
|
+
and the consumer deletes its row once the event is handed off. An event is pruned only when no
|
|
9
|
+
consumer still has it pending (or when it is older than `store.outbox.max_age_s`).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from sqlalchemy import BigInteger, ForeignKeyConstraint, Identity, Index, Integer, text
|
|
18
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
19
|
+
|
|
20
|
+
from lorebase_store.models.base import Base
|
|
21
|
+
from lorebase_store.models.enums import JOB_STATUS, JobStatus
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class OutboxEvent(Base):
|
|
25
|
+
"""Event written in the commit transaction and delivered later by Scribe."""
|
|
26
|
+
|
|
27
|
+
__tablename__ = "outbox"
|
|
28
|
+
__table_args__ = (Index(None, "created_at"),)
|
|
29
|
+
|
|
30
|
+
id: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True)
|
|
31
|
+
domain_id: Mapped[str]
|
|
32
|
+
event_type: Mapped[str]
|
|
33
|
+
payload: Mapped[dict[str, Any]]
|
|
34
|
+
created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class OutboxConsumer(Base):
|
|
38
|
+
"""A consumer of the outbox (the webhook worker is `webhooks`); events written after it
|
|
39
|
+
is registered are pending for it until it hands them off."""
|
|
40
|
+
|
|
41
|
+
__tablename__ = "outbox_consumers"
|
|
42
|
+
|
|
43
|
+
name: Mapped[str] = mapped_column(primary_key=True)
|
|
44
|
+
created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class OutboxPending(Base):
|
|
48
|
+
"""An event one consumer has not handed off yet (its delivery state)."""
|
|
49
|
+
|
|
50
|
+
__tablename__ = "outbox_pending"
|
|
51
|
+
__table_args__ = (
|
|
52
|
+
ForeignKeyConstraint(["consumer"], ["outbox_consumers.name"], ondelete="CASCADE"),
|
|
53
|
+
ForeignKeyConstraint(["event_id"], ["outbox.id"], ondelete="CASCADE"),
|
|
54
|
+
Index(None, "event_id"),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
consumer: Mapped[str] = mapped_column(primary_key=True)
|
|
58
|
+
event_id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
59
|
+
domain_id: Mapped[str]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Job(Base):
|
|
63
|
+
"""Background job; workers claim `queued` rows with `FOR UPDATE SKIP LOCKED`."""
|
|
64
|
+
|
|
65
|
+
__tablename__ = "jobs"
|
|
66
|
+
__table_args__ = (
|
|
67
|
+
Index(
|
|
68
|
+
"ix_jobs_claimable",
|
|
69
|
+
text("priority DESC"),
|
|
70
|
+
"run_after",
|
|
71
|
+
postgresql_where=text("status = 'queued'"),
|
|
72
|
+
),
|
|
73
|
+
Index(None, "domain_id", "type"),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
id: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True)
|
|
77
|
+
type: Mapped[str]
|
|
78
|
+
domain_id: Mapped[str | None]
|
|
79
|
+
payload: Mapped[dict[str, Any]] = mapped_column(server_default=text("'{}'::jsonb"))
|
|
80
|
+
status: Mapped[JobStatus] = mapped_column(JOB_STATUS, server_default=text("'queued'"))
|
|
81
|
+
priority: Mapped[int] = mapped_column(Integer, server_default=text("0"))
|
|
82
|
+
run_after: Mapped[datetime] = mapped_column(server_default=text("now()"))
|
|
83
|
+
attempts: Mapped[int] = mapped_column(Integer, server_default=text("0"))
|
|
84
|
+
locked_by: Mapped[str | None]
|
|
85
|
+
locked_at: Mapped[datetime | None]
|
|
86
|
+
result: Mapped[dict[str, Any] | None]
|
|
87
|
+
created_at: Mapped[datetime] = mapped_column(server_default=text("now()"))
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Agent trace steps (`keeper.trace`): how an agent session navigated the graph.
|
|
2
|
+
|
|
3
|
+
Debugging data, not compliance: rows are written best effort and pruned after the retention
|
|
4
|
+
period. Never values of Records: the query as the caller sent it (filter literals redacted
|
|
5
|
+
unless configured otherwise), result counts and the refs (Kind and key) a response touched.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from sqlalchemy import (
|
|
14
|
+
BigInteger,
|
|
15
|
+
CheckConstraint,
|
|
16
|
+
Double,
|
|
17
|
+
Identity,
|
|
18
|
+
Index,
|
|
19
|
+
Integer,
|
|
20
|
+
Text,
|
|
21
|
+
UniqueConstraint,
|
|
22
|
+
text,
|
|
23
|
+
)
|
|
24
|
+
from sqlalchemy.dialects.postgresql import ARRAY
|
|
25
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
26
|
+
|
|
27
|
+
from lorebase_store.models.base import Base
|
|
28
|
+
|
|
29
|
+
STEP_TYPES = ("call", "note")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AgentTraceStep(Base):
|
|
33
|
+
"""One traced request (`call`) or one note an agent attached to its session (`note`).
|
|
34
|
+
|
|
35
|
+
`domain_id` is null for notes and for calls outside a Domain; it has no foreign key, so
|
|
36
|
+
steps outlive a deleted Domain until the retention period ends.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
__tablename__ = "agent_trace_steps"
|
|
40
|
+
__table_args__ = (
|
|
41
|
+
CheckConstraint("type IN ('call', 'note')", name="type"),
|
|
42
|
+
Index(None, "actor", "session_id", "seq"),
|
|
43
|
+
Index(None, "session_id", "seq"),
|
|
44
|
+
Index(None, "domain_id", "at"),
|
|
45
|
+
Index(None, "at"),
|
|
46
|
+
Index(None, "actor", "fingerprint", "at", postgresql_where=text("inferred")),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
seq: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True)
|
|
50
|
+
session_id: Mapped[str]
|
|
51
|
+
actor: Mapped[str]
|
|
52
|
+
principal_type: Mapped[str | None]
|
|
53
|
+
domain_id: Mapped[str | None]
|
|
54
|
+
at: Mapped[datetime] = mapped_column(server_default=text("now()"))
|
|
55
|
+
type: Mapped[str]
|
|
56
|
+
tool: Mapped[str | None]
|
|
57
|
+
method: Mapped[str | None]
|
|
58
|
+
route: Mapped[str | None]
|
|
59
|
+
status: Mapped[int | None] = mapped_column(Integer)
|
|
60
|
+
latency_ms: Mapped[float | None] = mapped_column(Double)
|
|
61
|
+
cache: Mapped[str | None]
|
|
62
|
+
branch: Mapped[str | None]
|
|
63
|
+
query: Mapped[dict[str, Any] | None]
|
|
64
|
+
result: Mapped[dict[str, Any] | None]
|
|
65
|
+
refs: Mapped[dict[str, Any] | None]
|
|
66
|
+
explain: Mapped[dict[str, Any] | None]
|
|
67
|
+
note: Mapped[str | None]
|
|
68
|
+
proposal_id: Mapped[str | None]
|
|
69
|
+
request_id: Mapped[str | None]
|
|
70
|
+
trace_id: Mapped[str | None]
|
|
71
|
+
inferred: Mapped[bool] = mapped_column(server_default=text("false"))
|
|
72
|
+
"""The session was inferred (no session header): grouped by principal and client
|
|
73
|
+
fingerprint while requests keep coming within `keeper.trace.inferred_session_idle_s`."""
|
|
74
|
+
fingerprint: Mapped[str | None]
|
|
75
|
+
"""Digest of the principal, its credential and user agent; set on inferred steps."""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AgentTraceSession(Base):
|
|
79
|
+
"""One session of one actor: the number of steps it holds, which caps it.
|
|
80
|
+
|
|
81
|
+
The trace writer raises `steps` in the transaction that writes the steps, under the row
|
|
82
|
+
lock, and skips the steps past `keeper.trace.max_steps_per_session`; pruning lowers it by
|
|
83
|
+
the steps it deletes and removes the row once none is left.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
__tablename__ = "agent_trace_sessions"
|
|
87
|
+
|
|
88
|
+
actor: Mapped[str] = mapped_column(primary_key=True)
|
|
89
|
+
session_id: Mapped[str] = mapped_column(primary_key=True)
|
|
90
|
+
steps: Mapped[int] = mapped_column(Integer)
|
|
91
|
+
last_at: Mapped[datetime]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class AgentTraceSessionDomain(Base):
|
|
95
|
+
"""Summary of the steps of one session of one actor in one Domain (`domain_id` null: steps
|
|
96
|
+
outside any Domain), kept by the trace writer and pruning. Sessions are listed from these
|
|
97
|
+
rows, which visibility filters like steps (by actor and Domain)."""
|
|
98
|
+
|
|
99
|
+
__tablename__ = "agent_trace_session_domains"
|
|
100
|
+
__table_args__ = (
|
|
101
|
+
UniqueConstraint("actor", "session_id", "domain_id", postgresql_nulls_not_distinct=True),
|
|
102
|
+
Index(None, "actor", "last_at"),
|
|
103
|
+
Index(None, "domain_id", "last_at"),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
id: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True)
|
|
107
|
+
actor: Mapped[str]
|
|
108
|
+
session_id: Mapped[str]
|
|
109
|
+
domain_id: Mapped[str | None]
|
|
110
|
+
started_at: Mapped[datetime]
|
|
111
|
+
last_at: Mapped[datetime]
|
|
112
|
+
calls: Mapped[int] = mapped_column(Integer)
|
|
113
|
+
notes: Mapped[int] = mapped_column(Integer)
|
|
114
|
+
inferred: Mapped[bool]
|
|
115
|
+
tools: Mapped[list[str]] = mapped_column(ARRAY(Text))
|
|
116
|
+
proposals: Mapped[list[str]] = mapped_column(ARRAY(Text))
|