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,168 @@
|
|
|
1
|
+
"""Reviewer decisions on extraction items (design 9.2 step 12, 12.3 Review).
|
|
2
|
+
|
|
3
|
+
`decide_items` records accept, reject or reset decisions (with optional edits) on items of an
|
|
4
|
+
open extraction Proposal, re-materializes the extraction branch so it holds exactly the
|
|
5
|
+
accepted items, and refreshes the Proposal (summary, checks, merge preview), all in the
|
|
6
|
+
caller's transaction. When the branch rejects the accepted set (for example a new Record that
|
|
7
|
+
misses a required value), the commit raises `ValidationFailedError` and the caller's
|
|
8
|
+
transaction must roll back: decisions and branch stay as they were.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from pydantic import ValidationError
|
|
19
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
20
|
+
|
|
21
|
+
from lorebase_core.merge import MergePolicy
|
|
22
|
+
from lorebase_store.commit.schema import load_compiled
|
|
23
|
+
from lorebase_store.extraction.items import ItemEditError, normalize_edits
|
|
24
|
+
from lorebase_store.extraction.materialize import (
|
|
25
|
+
MaterializeContext,
|
|
26
|
+
Materialized,
|
|
27
|
+
materialize_items,
|
|
28
|
+
)
|
|
29
|
+
from lorebase_store.extraction.proposal import refresh_proposal
|
|
30
|
+
from lorebase_store.merging import AttemptResult
|
|
31
|
+
from lorebase_store.models.enums import ItemDecision, ProposalOrigin, ProposalStatus
|
|
32
|
+
from lorebase_store.repositories.documents import DocumentRepository, ExtractionJobRepository
|
|
33
|
+
from lorebase_store.repositories.materialized import MaterializedRepository
|
|
34
|
+
from lorebase_store.repositories.proposal_items import ItemRow, ProposalItemRepository
|
|
35
|
+
from lorebase_store.repositories.proposals import ProposalRepository, ProposalRow
|
|
36
|
+
from lorebase_store.repositories.scope import scope_to_domain
|
|
37
|
+
from lorebase_store.runtime import StoreRuntime
|
|
38
|
+
|
|
39
|
+
MERGE_POLICY_KEY = "merge_policy"
|
|
40
|
+
"""Key of the merge policy in a Proposal's `policies`."""
|
|
41
|
+
UNKNOWN_PROMPT = "unknown"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ReviewError(Exception):
|
|
45
|
+
"""A decision request that cannot be applied; `reason` names the case."""
|
|
46
|
+
|
|
47
|
+
def __init__(self, reason: str, message: str, item_ids: Sequence[str] = ()) -> None:
|
|
48
|
+
super().__init__(message)
|
|
49
|
+
self.reason = reason
|
|
50
|
+
self.item_ids = list(item_ids)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True, slots=True)
|
|
54
|
+
class Decision:
|
|
55
|
+
"""One decision; `edits` of `None` keeps the item's stored edits, `{}` clears them."""
|
|
56
|
+
|
|
57
|
+
item_id: str
|
|
58
|
+
decision: ItemDecision
|
|
59
|
+
edits: Mapping[str, Any] | None = None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True)
|
|
63
|
+
class Reviewed:
|
|
64
|
+
proposal: ProposalRow
|
|
65
|
+
items: list[ItemRow]
|
|
66
|
+
outcome: Materialized
|
|
67
|
+
attempt: AttemptResult | None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def extraction_proposal(
|
|
71
|
+
session: AsyncSession, domain_id: str, proposal_id: str, *, for_update: bool = False
|
|
72
|
+
) -> ProposalRow:
|
|
73
|
+
"""The extraction Proposal, or `ReviewError` (`not_found`, `not_extraction`)."""
|
|
74
|
+
proposal = await ProposalRepository(session, domain_id).get(proposal_id, for_update=for_update)
|
|
75
|
+
if proposal is None:
|
|
76
|
+
raise ReviewError("not_found", f"Proposal {proposal_id!r} not found.")
|
|
77
|
+
if proposal.origin is not ProposalOrigin.EXTRACTION:
|
|
78
|
+
raise ReviewError("not_extraction", f"Proposal {proposal_id!r} has no extraction items.")
|
|
79
|
+
return proposal
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def decide_items(
|
|
83
|
+
session: AsyncSession,
|
|
84
|
+
domain_id: str,
|
|
85
|
+
proposal_id: str,
|
|
86
|
+
decisions: Sequence[Decision],
|
|
87
|
+
*,
|
|
88
|
+
actor: str,
|
|
89
|
+
runtime: StoreRuntime,
|
|
90
|
+
clock: Callable[[], datetime],
|
|
91
|
+
) -> Reviewed:
|
|
92
|
+
"""Apply `decisions`, re-materialize the extraction branch and refresh the Proposal.
|
|
93
|
+
|
|
94
|
+
Raises `ReviewError` (`not_found`, `not_extraction`, `not_open`, `unknown_items`,
|
|
95
|
+
`invalid_edits`), `ValidationFailedError` and the commit pipeline's other errors.
|
|
96
|
+
"""
|
|
97
|
+
await scope_to_domain(session, domain_id)
|
|
98
|
+
proposal = await extraction_proposal(session, domain_id, proposal_id, for_update=True)
|
|
99
|
+
if proposal.status is not ProposalStatus.OPEN:
|
|
100
|
+
raise ReviewError("not_open", f"Proposal {proposal_id!r} is {proposal.status.value}.")
|
|
101
|
+
writes = runtime.settings.writes
|
|
102
|
+
compiled = await MaterializedRepository(
|
|
103
|
+
session, domain_id, proposal.target_ref, writes=writes
|
|
104
|
+
).schema()
|
|
105
|
+
if compiled is None:
|
|
106
|
+
raise ReviewError("not_found", f"branch {proposal.target_ref!r} is not materialized")
|
|
107
|
+
schema = load_compiled(compiled.compiled)
|
|
108
|
+
repository = ProposalItemRepository(session, domain_id)
|
|
109
|
+
stored = {item.item_id: item for item in await repository.all(proposal_id, for_update=True)}
|
|
110
|
+
unknown = sorted({d.item_id for d in decisions} - stored.keys())
|
|
111
|
+
if unknown:
|
|
112
|
+
raise ReviewError("unknown_items", "Items not in the Proposal.", unknown)
|
|
113
|
+
now = clock()
|
|
114
|
+
changed: list[str] = []
|
|
115
|
+
for decision in decisions:
|
|
116
|
+
item = stored[decision.item_id]
|
|
117
|
+
try:
|
|
118
|
+
edits = (
|
|
119
|
+
item.edits
|
|
120
|
+
if decision.edits is None
|
|
121
|
+
else normalize_edits(item, decision.edits, schema) or None
|
|
122
|
+
)
|
|
123
|
+
except ItemEditError as error:
|
|
124
|
+
raise ReviewError("invalid_edits", str(error), [error.item_id]) from None
|
|
125
|
+
pending = decision.decision is ItemDecision.PENDING
|
|
126
|
+
await repository.decide(
|
|
127
|
+
proposal_id,
|
|
128
|
+
item.item_id,
|
|
129
|
+
decision=decision.decision,
|
|
130
|
+
decided_by=None if pending else actor,
|
|
131
|
+
decided_at=None if pending else now,
|
|
132
|
+
edits=edits,
|
|
133
|
+
)
|
|
134
|
+
changed.append(item.item_id)
|
|
135
|
+
items = await repository.all(proposal_id)
|
|
136
|
+
job = await ExtractionJobRepository(session, domain_id).by_proposal(proposal_id)
|
|
137
|
+
context = MaterializeContext(
|
|
138
|
+
domain_id=domain_id,
|
|
139
|
+
branch=proposal.source_ref,
|
|
140
|
+
target=proposal.target_ref,
|
|
141
|
+
job_id=job.id if job is not None else proposal.source_ref,
|
|
142
|
+
prompt_version=job.prompt_version if job is not None else UNKNOWN_PROMPT,
|
|
143
|
+
actor=actor,
|
|
144
|
+
runtime=runtime,
|
|
145
|
+
clock=clock,
|
|
146
|
+
)
|
|
147
|
+
outcome = await materialize_items(
|
|
148
|
+
session,
|
|
149
|
+
context,
|
|
150
|
+
items,
|
|
151
|
+
schema,
|
|
152
|
+
message=f"Review of {proposal_id}: {len(changed)} decision(s)",
|
|
153
|
+
)
|
|
154
|
+
documents: list[str] = []
|
|
155
|
+
if job is not None:
|
|
156
|
+
found = DocumentRepository(session, domain_id)
|
|
157
|
+
for document_id in job.documents:
|
|
158
|
+
row = await found.get(document_id)
|
|
159
|
+
documents.append(row.filename if row is not None else document_id)
|
|
160
|
+
try:
|
|
161
|
+
policy = MergePolicy.model_validate((proposal.policies or {}).get(MERGE_POLICY_KEY) or {})
|
|
162
|
+
except ValidationError:
|
|
163
|
+
policy = MergePolicy()
|
|
164
|
+
final, attempt = await refresh_proposal(
|
|
165
|
+
session, context, proposal, items, schema, outcome, documents=documents, policy=policy
|
|
166
|
+
)
|
|
167
|
+
by_id = {item.item_id: item for item in items}
|
|
168
|
+
return Reviewed(final, [by_id[i] for i in changed], outcome, attempt)
|