rememberstack 0.1.0__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.
- rememberstack/__init__.py +9 -0
- rememberstack/adapters/__init__.py +42 -0
- rememberstack/adapters/codex_writer.py +221 -0
- rememberstack/adapters/markitdown_converter.py +42 -0
- rememberstack/adapters/openrouter.py +136 -0
- rememberstack/adapters/selfhost/__init__.py +54 -0
- rememberstack/adapters/selfhost/forget.py +66 -0
- rememberstack/adapters/selfhost/git.py +374 -0
- rememberstack/adapters/selfhost/lance.py +328 -0
- rememberstack/adapters/selfhost/minio.py +279 -0
- rememberstack/adapters/selfhost/mounts.py +249 -0
- rememberstack/adapters/selfhost/object_store.py +130 -0
- rememberstack/adapters/selfhost/projection.py +80 -0
- rememberstack/adapters/selfhost/queue.py +137 -0
- rememberstack/adapters/selfhost/telemetry.py +45 -0
- rememberstack/adapters/selfhost/watcher.py +70 -0
- rememberstack/adapters/testing/__init__.py +15 -0
- rememberstack/adapters/testing/cost_meter.py +13 -0
- rememberstack/adapters/testing/model_provider.py +83 -0
- rememberstack/adapters/testing/queue.py +43 -0
- rememberstack/adapters/testing/telemetry.py +22 -0
- rememberstack/client.py +19 -0
- rememberstack/core/__init__.py +127 -0
- rememberstack/core/blockizer.py +189 -0
- rememberstack/core/chunker.py +216 -0
- rememberstack/core/consumption_skill.py +275 -0
- rememberstack/core/conversion.py +76 -0
- rememberstack/core/core_manifest.py +598 -0
- rememberstack/core/extension_packs.py +124 -0
- rememberstack/core/forget.py +17 -0
- rememberstack/core/knowledge_authored.py +276 -0
- rememberstack/core/knowledge_compile.py +215 -0
- rememberstack/core/knowledge_fact_sheet.py +210 -0
- rememberstack/core/knowledge_hashing.py +68 -0
- rememberstack/core/knowledge_planner.py +64 -0
- rememberstack/core/knowledge_writer.py +175 -0
- rememberstack/core/ranking.py +200 -0
- rememberstack/core/recipe_linter.py +149 -0
- rememberstack/core/section_snap.py +209 -0
- rememberstack/core/storage_routing.py +27 -0
- rememberstack/eval/__init__.py +53 -0
- rememberstack/eval/consumption.py +141 -0
- rememberstack/eval/contradiction.py +184 -0
- rememberstack/eval/harness.py +136 -0
- rememberstack/eval/lifecycle.py +400 -0
- rememberstack/eval/operational_scale.py +49 -0
- rememberstack/eval/resolution.py +255 -0
- rememberstack/eval/retrieval_spikes.py +50 -0
- rememberstack/eval/skeleton.py +231 -0
- rememberstack/llm/__init__.py +1 -0
- rememberstack/model/__init__.py +589 -0
- rememberstack/model/adjudication.py +100 -0
- rememberstack/model/auth.py +27 -0
- rememberstack/model/blocks.py +30 -0
- rememberstack/model/chunks.py +190 -0
- rememberstack/model/claims.py +162 -0
- rememberstack/model/client.py +98 -0
- rememberstack/model/clustering.py +54 -0
- rememberstack/model/component_version.py +124 -0
- rememberstack/model/consumption.py +88 -0
- rememberstack/model/conversion.py +31 -0
- rememberstack/model/deployment.py +53 -0
- rememberstack/model/documents.py +168 -0
- rememberstack/model/envelope.py +513 -0
- rememberstack/model/evaluation.py +72 -0
- rememberstack/model/forget.py +143 -0
- rememberstack/model/git.py +13 -0
- rememberstack/model/knowledge.py +840 -0
- rememberstack/model/knowledge_authored.py +325 -0
- rememberstack/model/knowledge_planner.py +431 -0
- rememberstack/model/lifecycle.py +42 -0
- rememberstack/model/model_provider.py +78 -0
- rememberstack/model/mounts.py +24 -0
- rememberstack/model/object_store.py +21 -0
- rememberstack/model/operational_scale.py +59 -0
- rememberstack/model/operations.py +153 -0
- rememberstack/model/processing.py +228 -0
- rememberstack/model/queue.py +73 -0
- rememberstack/model/recipes.py +83 -0
- rememberstack/model/relations.py +79 -0
- rememberstack/model/resolution.py +83 -0
- rememberstack/model/retrieval_spikes.py +62 -0
- rememberstack/model/sections.py +120 -0
- rememberstack/model/telemetry.py +30 -0
- rememberstack/ports/__init__.py +29 -0
- rememberstack/ports/auth.py +16 -0
- rememberstack/ports/connector.py +23 -0
- rememberstack/ports/cost_meter.py +17 -0
- rememberstack/ports/forget.py +20 -0
- rememberstack/ports/git.py +20 -0
- rememberstack/ports/model_provider.py +28 -0
- rememberstack/ports/mounts.py +16 -0
- rememberstack/ports/object_store.py +27 -0
- rememberstack/ports/p1_index.py +92 -0
- rememberstack/ports/purge.py +93 -0
- rememberstack/ports/queue.py +23 -0
- rememberstack/ports/telemetry.py +21 -0
- rememberstack/profiles/__init__.py +22 -0
- rememberstack/profiles/selfhost.py +324 -0
- rememberstack/profiles/selfhost_forget.py +158 -0
- rememberstack/profiles/selfhost_operations.py +95 -0
- rememberstack/py.typed +1 -0
- rememberstack/spine/__init__.py +93 -0
- rememberstack/spine/admission.py +26 -0
- rememberstack/spine/backfill.py +168 -0
- rememberstack/spine/catalog_contract.py +742 -0
- rememberstack/spine/chunk_catalog.py +237 -0
- rememberstack/spine/claim_catalog.py +298 -0
- rememberstack/spine/clustering.py +740 -0
- rememberstack/spine/component_versions.py +208 -0
- rememberstack/spine/consumption.py +81 -0
- rememberstack/spine/deployment_bootstrap.py +445 -0
- rememberstack/spine/document_catalog.py +621 -0
- rememberstack/spine/entity_registry.py +205 -0
- rememberstack/spine/extension_packs.py +220 -0
- rememberstack/spine/fact_catalog.py +571 -0
- rememberstack/spine/forget.py +1753 -0
- rememberstack/spine/knowledge.py +5467 -0
- rememberstack/spine/lifecycle.py +1071 -0
- rememberstack/spine/migrations/__init__.py +1 -0
- rememberstack/spine/migrations/_helpers.py +153 -0
- rememberstack/spine/migrations/env.py +58 -0
- rememberstack/spine/migrations/script.py.mako +27 -0
- rememberstack/spine/migrations/versions/__init__.py +1 -0
- rememberstack/spine/migrations/versions/p0_02_0001_extensions_enums.py +189 -0
- rememberstack/spine/migrations/versions/p0_02_0002_infrastructure_registries.py +321 -0
- rememberstack/spine/migrations/versions/p0_02_0003_entities_evaluation_e0_e1.py +631 -0
- rememberstack/spine/migrations/versions/p0_02_0004_claims_facts_evidence.py +411 -0
- rememberstack/spine/migrations/versions/p0_02_0005_projection_knowledge_retrieval.py +391 -0
- rememberstack/spine/migrations/versions/p0_02_0006_partitions_views.py +158 -0
- rememberstack/spine/migrations/versions/p2_06_0007_invalidated_outcome.py +26 -0
- rememberstack/spine/migrations/versions/p3_01_0008_document_version_target.py +58 -0
- rememberstack/spine/migrations/versions/p3_05_0009_reconcile_stage.py +27 -0
- rememberstack/spine/migrations/versions/p3_07_0010_lifecycle_eval_suite.py +25 -0
- rememberstack/spine/migrations/versions/p4_01_0011_survivor_view_rewrite.py +57 -0
- rememberstack/spine/migrations/versions/p6_02_0012_knowledge_compile_recovery.py +58 -0
- rememberstack/spine/migrations/versions/p6_04_0013_knowledge_writer_ledger.py +46 -0
- rememberstack/spine/migrations/versions/p6_05_0014_knowledge_planner_runtime.py +217 -0
- rememberstack/spine/migrations/versions/p6_06_0015_authored_dispatch_runtime.py +38 -0
- rememberstack/spine/migrations/versions/p7_02_0016_operational_eval_suite.py +19 -0
- rememberstack/spine/migrations/versions/p7_05_0017_hard_forget.py +55 -0
- rememberstack/spine/observation_adjudication.py +778 -0
- rememberstack/spine/operations.py +298 -0
- rememberstack/spine/projection.py +662 -0
- rememberstack/spine/recipes.py +276 -0
- rememberstack/spine/resolver.py +763 -0
- rememberstack/spine/review.py +650 -0
- rememberstack/spine/settings.py +22 -0
- rememberstack/spine/supersession.py +510 -0
- rememberstack/spine/sync.py +128 -0
- rememberstack/spine/work_ledger.py +816 -0
- rememberstack/surfaces/__init__.py +110 -0
- rememberstack/surfaces/cli.py +447 -0
- rememberstack/surfaces/consumption_skill.py +87 -0
- rememberstack/surfaces/graph_queries.py +698 -0
- rememberstack/surfaces/http_api.py +377 -0
- rememberstack/surfaces/mcp.py +67 -0
- rememberstack/surfaces/query_engine.py +1591 -0
- rememberstack/surfaces/recipe_executor.py +185 -0
- rememberstack/surfaces/recipe_surface.py +219 -0
- rememberstack/surfaces/remote_mcp.py +133 -0
- rememberstack/surfaces/sdk.py +324 -0
- rememberstack/workers/__init__.py +155 -0
- rememberstack/workers/base.py +312 -0
- rememberstack/workers/e0.py +577 -0
- rememberstack/workers/e1.py +425 -0
- rememberstack/workers/e2.py +525 -0
- rememberstack/workers/e3.py +434 -0
- rememberstack/workers/forget.py +299 -0
- rememberstack/workers/knowledge_authored.py +146 -0
- rememberstack/workers/knowledge_driver.py +735 -0
- rememberstack/workers/knowledge_fact_sheet.py +123 -0
- rememberstack/workers/knowledge_planner.py +325 -0
- rememberstack/workers/knowledge_writer.py +393 -0
- rememberstack/workers/operations.py +42 -0
- rememberstack/workers/p1.py +234 -0
- rememberstack/workers/p2.py +513 -0
- rememberstack/workers/p2_analytics.py +276 -0
- rememberstack/workers/p3.py +673 -0
- rememberstack/workers/reconcile.py +485 -0
- rememberstack/workers/sync.py +168 -0
- rememberstack-0.1.0.dist-info/METADATA +213 -0
- rememberstack-0.1.0.dist-info/RECORD +186 -0
- rememberstack-0.1.0.dist-info/WHEEL +4 -0
- rememberstack-0.1.0.dist-info/entry_points.txt +2 -0
- rememberstack-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"""Typed Plane-K planner, reflection, and quarantine values (D45/D46)."""
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from pathlib import PurePosixPath
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
from typing import Literal
|
|
8
|
+
from typing import TypeAlias
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from pydantic import AfterValidator
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
from pydantic import ConfigDict
|
|
14
|
+
from pydantic import Field
|
|
15
|
+
from pydantic import field_validator
|
|
16
|
+
from pydantic import model_validator
|
|
17
|
+
|
|
18
|
+
from rememberstack.model.knowledge import KnowledgeAgentSandboxPolicy
|
|
19
|
+
from rememberstack.model.knowledge import KnowledgeAgentSessionRequest
|
|
20
|
+
from rememberstack.model.knowledge import KnowledgeArtifactStatus
|
|
21
|
+
from rememberstack.model.knowledge import KnowledgeLayer
|
|
22
|
+
from rememberstack.model.knowledge import KnowledgePageKind
|
|
23
|
+
from rememberstack.model.knowledge import KnowledgePlanAction
|
|
24
|
+
from rememberstack.model.knowledge import KnowledgePlanStatus
|
|
25
|
+
from rememberstack.model.knowledge import KnowledgePlanTrigger
|
|
26
|
+
from rememberstack.model.knowledge import KnowledgeRuleParams
|
|
27
|
+
from rememberstack.model.knowledge import KnowledgeWriterSuggestion
|
|
28
|
+
from rememberstack.model.queue import UTCDateTime
|
|
29
|
+
|
|
30
|
+
KNOWLEDGE_PLANNER_OUTPUT_PATHS = ("output/decisions.json",)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _canonical_uuids(values: tuple[UUID, ...]) -> tuple[UUID, ...]:
|
|
34
|
+
"""Return stable unique UUIDs for set-valued planner fields."""
|
|
35
|
+
return tuple(sorted(set(values), key=str))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
CanonicalUUIDs: TypeAlias = Annotated[
|
|
39
|
+
tuple[UUID, ...], AfterValidator(_canonical_uuids)
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _normalized_git_path(*, value: str, field: str) -> str:
|
|
44
|
+
"""Reject absolute, escaping, or non-normalized repository paths."""
|
|
45
|
+
path = PurePosixPath(value)
|
|
46
|
+
if path.is_absolute() or ".." in path.parts or str(path) != value:
|
|
47
|
+
raise ValueError(f"{field} must be a normalized repository path")
|
|
48
|
+
return value
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class KnowledgePlanRunKind(StrEnum):
|
|
52
|
+
"""The proposing seat that produced one structural planning run."""
|
|
53
|
+
|
|
54
|
+
PLANNER = "planner"
|
|
55
|
+
REFLECTION = "reflection"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class KnowledgePlanRunStatus(StrEnum):
|
|
59
|
+
"""Terminal state of one transcript-bearing planning run."""
|
|
60
|
+
|
|
61
|
+
SUCCEEDED = "succeeded"
|
|
62
|
+
FAILED = "failed"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class KnowledgePlanBand(StrEnum):
|
|
66
|
+
"""Deterministic D24-style consequence of a structural proposal."""
|
|
67
|
+
|
|
68
|
+
AUTO_APPLY = "auto_apply"
|
|
69
|
+
REVIEW = "review"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class KnowledgeQuarantineStatus(StrEnum):
|
|
73
|
+
"""Resolution state of a direct edit to a compiled page."""
|
|
74
|
+
|
|
75
|
+
PROPOSED = "proposed"
|
|
76
|
+
CURATION_ACCEPTED = "curation_accepted"
|
|
77
|
+
ADOPTED = "adopted"
|
|
78
|
+
REJECTED = "rejected"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class KnowledgePlannedPage(BaseModel):
|
|
82
|
+
"""One compiled page and its complete mechanically evaluable rule set."""
|
|
83
|
+
|
|
84
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
85
|
+
|
|
86
|
+
layer: KnowledgeLayer
|
|
87
|
+
git_path: str = Field(min_length=1)
|
|
88
|
+
curation_path: str = Field(min_length=1)
|
|
89
|
+
writer_version: str = Field(min_length=1)
|
|
90
|
+
rules: tuple[KnowledgeRuleParams, ...] = Field(min_length=1)
|
|
91
|
+
scope_id: UUID | None = None
|
|
92
|
+
parent_artifact_id: UUID | None = None
|
|
93
|
+
artifact_kind: str | None = None
|
|
94
|
+
|
|
95
|
+
@field_validator("git_path", "curation_path")
|
|
96
|
+
@classmethod
|
|
97
|
+
def require_safe_paths(cls, value: str, info: object) -> str:
|
|
98
|
+
"""Keep planner-created files inside the repository namespace."""
|
|
99
|
+
field_name = getattr(info, "field_name", "path")
|
|
100
|
+
return _normalized_git_path(value=value, field=str(field_name))
|
|
101
|
+
|
|
102
|
+
@model_validator(mode="after")
|
|
103
|
+
def require_disjoint_body_and_curation_paths(self) -> "KnowledgePlannedPage":
|
|
104
|
+
"""Prevent the machine-owned body from aliasing its human-owned sidecar."""
|
|
105
|
+
if self.git_path == self.curation_path:
|
|
106
|
+
raise ValueError("planned page body and curation paths must differ")
|
|
107
|
+
return self
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class _KnowledgePlanProposalBase(BaseModel):
|
|
111
|
+
"""Fields every planner or reflection proposal must justify."""
|
|
112
|
+
|
|
113
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
114
|
+
|
|
115
|
+
rationale: str = Field(min_length=1)
|
|
116
|
+
confidence: Decimal = Field(ge=Decimal("0"), le=Decimal("1"))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class KnowledgeCreatePageProposal(_KnowledgePlanProposalBase):
|
|
120
|
+
"""Create one evidence-derived compiled page."""
|
|
121
|
+
|
|
122
|
+
action: Literal[KnowledgePlanAction.CREATE_PAGE] = KnowledgePlanAction.CREATE_PAGE
|
|
123
|
+
page: KnowledgePlannedPage
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class KnowledgeSplitPageProposal(_KnowledgePlanProposalBase):
|
|
127
|
+
"""Turn one compiled page into a parent over new mechanically routed children."""
|
|
128
|
+
|
|
129
|
+
action: Literal[KnowledgePlanAction.SPLIT_PAGE] = KnowledgePlanAction.SPLIT_PAGE
|
|
130
|
+
source_artifact_id: UUID
|
|
131
|
+
pages: tuple[KnowledgePlannedPage, ...] = Field(min_length=1)
|
|
132
|
+
|
|
133
|
+
@model_validator(mode="after")
|
|
134
|
+
def require_children_of_source(self) -> "KnowledgeSplitPageProposal":
|
|
135
|
+
"""Make the split topology explicit instead of guessing parentage at apply time."""
|
|
136
|
+
if any(
|
|
137
|
+
page.parent_artifact_id != self.source_artifact_id for page in self.pages
|
|
138
|
+
):
|
|
139
|
+
raise ValueError("every split page must name the source page as parent")
|
|
140
|
+
paths = [page.git_path for page in self.pages]
|
|
141
|
+
curation_paths = [page.curation_path for page in self.pages]
|
|
142
|
+
if len(set(paths)) != len(paths) or len(set(curation_paths)) != len(
|
|
143
|
+
curation_paths
|
|
144
|
+
):
|
|
145
|
+
raise ValueError("split page paths must be unique")
|
|
146
|
+
return self
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class KnowledgeMergePagesProposal(_KnowledgePlanProposalBase):
|
|
150
|
+
"""Retire several compiled pages into one new compiled target."""
|
|
151
|
+
|
|
152
|
+
action: Literal[KnowledgePlanAction.MERGE_PAGES] = KnowledgePlanAction.MERGE_PAGES
|
|
153
|
+
source_artifact_ids: CanonicalUUIDs = Field(min_length=2)
|
|
154
|
+
page: KnowledgePlannedPage
|
|
155
|
+
|
|
156
|
+
@model_validator(mode="after")
|
|
157
|
+
def reject_source_as_target_parent(self) -> "KnowledgeMergePagesProposal":
|
|
158
|
+
"""Prevent an immediate cycle through a page the merge will tombstone."""
|
|
159
|
+
if self.page.parent_artifact_id in self.source_artifact_ids:
|
|
160
|
+
raise ValueError("merge target parent cannot be a merge source")
|
|
161
|
+
return self
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class KnowledgeMovePageProposal(_KnowledgePlanProposalBase):
|
|
165
|
+
"""Move one compiled page without changing its evidence ownership."""
|
|
166
|
+
|
|
167
|
+
action: Literal[KnowledgePlanAction.MOVE_PAGE] = KnowledgePlanAction.MOVE_PAGE
|
|
168
|
+
artifact_id: UUID
|
|
169
|
+
old_git_path: str = Field(min_length=1)
|
|
170
|
+
new_git_path: str = Field(min_length=1)
|
|
171
|
+
old_curation_path: str = Field(min_length=1)
|
|
172
|
+
new_curation_path: str = Field(min_length=1)
|
|
173
|
+
old_parent_artifact_id: UUID | None
|
|
174
|
+
new_parent_artifact_id: UUID | None = None
|
|
175
|
+
|
|
176
|
+
@field_validator(
|
|
177
|
+
"old_git_path", "new_git_path", "old_curation_path", "new_curation_path"
|
|
178
|
+
)
|
|
179
|
+
@classmethod
|
|
180
|
+
def require_safe_paths(cls, value: str, info: object) -> str:
|
|
181
|
+
"""Keep both sides of a move inside the repository namespace."""
|
|
182
|
+
field_name = getattr(info, "field_name", "path")
|
|
183
|
+
return _normalized_git_path(value=value, field=str(field_name))
|
|
184
|
+
|
|
185
|
+
@model_validator(mode="after")
|
|
186
|
+
def require_a_real_move(self) -> "KnowledgeMovePageProposal":
|
|
187
|
+
"""Reject a path-preserving move that changes no structure."""
|
|
188
|
+
if self.new_parent_artifact_id == self.artifact_id:
|
|
189
|
+
raise ValueError("move_page cannot make an artifact its own parent")
|
|
190
|
+
if self.old_git_path == self.new_git_path and (
|
|
191
|
+
self.old_curation_path == self.new_curation_path
|
|
192
|
+
and self.old_parent_artifact_id == self.new_parent_artifact_id
|
|
193
|
+
):
|
|
194
|
+
raise ValueError("move_page must change path or parent")
|
|
195
|
+
if self.new_git_path == self.new_curation_path:
|
|
196
|
+
raise ValueError("moved body and curation paths must differ")
|
|
197
|
+
return self
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class KnowledgeRetirePageProposal(_KnowledgePlanProposalBase):
|
|
201
|
+
"""Retire one compiled page whose evidence no longer needs a home."""
|
|
202
|
+
|
|
203
|
+
action: Literal[KnowledgePlanAction.RETIRE_PAGE] = KnowledgePlanAction.RETIRE_PAGE
|
|
204
|
+
artifact_id: UUID
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class KnowledgeAdjustRuleProposal(_KnowledgePlanProposalBase):
|
|
208
|
+
"""Replace one compiled page's routing-rule union atomically."""
|
|
209
|
+
|
|
210
|
+
action: Literal[KnowledgePlanAction.ADJUST_RULE] = KnowledgePlanAction.ADJUST_RULE
|
|
211
|
+
artifact_id: UUID
|
|
212
|
+
rules: tuple[KnowledgeRuleParams, ...] = Field(min_length=1)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class KnowledgeConvertKindProposal(_KnowledgePlanProposalBase):
|
|
216
|
+
"""Adopt a compiled page or hand an authored page to the compiler."""
|
|
217
|
+
|
|
218
|
+
action: Literal[KnowledgePlanAction.CONVERT_KIND] = KnowledgePlanAction.CONVERT_KIND
|
|
219
|
+
artifact_id: UUID
|
|
220
|
+
from_kind: KnowledgePageKind
|
|
221
|
+
to_kind: KnowledgePageKind
|
|
222
|
+
writer_version: str | None = None
|
|
223
|
+
curation_path: str | None = None
|
|
224
|
+
rules: tuple[KnowledgeRuleParams, ...] = ()
|
|
225
|
+
|
|
226
|
+
@field_validator("curation_path")
|
|
227
|
+
@classmethod
|
|
228
|
+
def require_safe_optional_curation_path(cls, value: str | None) -> str | None:
|
|
229
|
+
"""Keep handover sidecars inside the repository namespace."""
|
|
230
|
+
if value is None:
|
|
231
|
+
return None
|
|
232
|
+
return _normalized_git_path(value=value, field="curation_path")
|
|
233
|
+
|
|
234
|
+
@model_validator(mode="after")
|
|
235
|
+
def require_one_valid_direction(self) -> "KnowledgeConvertKindProposal":
|
|
236
|
+
"""Bind adoption and handover to their distinct ownership inputs."""
|
|
237
|
+
if self.from_kind == self.to_kind:
|
|
238
|
+
raise ValueError("convert_kind must change page kind")
|
|
239
|
+
if self.to_kind is KnowledgePageKind.AUTHORED:
|
|
240
|
+
if (
|
|
241
|
+
self.writer_version is not None
|
|
242
|
+
or self.curation_path is not None
|
|
243
|
+
or self.rules
|
|
244
|
+
):
|
|
245
|
+
raise ValueError("adoption preserves rules and removes writer inputs")
|
|
246
|
+
return self
|
|
247
|
+
if self.writer_version is None or self.curation_path is None or not self.rules:
|
|
248
|
+
raise ValueError(
|
|
249
|
+
"handover requires writer version, curation path, and rules"
|
|
250
|
+
)
|
|
251
|
+
return self
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
KnowledgePlanProposal: TypeAlias = Annotated[
|
|
255
|
+
KnowledgeCreatePageProposal
|
|
256
|
+
| KnowledgeSplitPageProposal
|
|
257
|
+
| KnowledgeMergePagesProposal
|
|
258
|
+
| KnowledgeMovePageProposal
|
|
259
|
+
| KnowledgeRetirePageProposal
|
|
260
|
+
| KnowledgeAdjustRuleProposal
|
|
261
|
+
| KnowledgeConvertKindProposal,
|
|
262
|
+
Field(discriminator="action"),
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class KnowledgePlannerSandboxPolicy(KnowledgeAgentSandboxPolicy):
|
|
267
|
+
"""The fixed declared output surface of a planner or reflection session."""
|
|
268
|
+
|
|
269
|
+
accepted_output_paths: tuple[str, ...] = KNOWLEDGE_PLANNER_OUTPUT_PATHS
|
|
270
|
+
|
|
271
|
+
@field_validator("accepted_output_paths")
|
|
272
|
+
@classmethod
|
|
273
|
+
def require_declared_planner_output(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
274
|
+
"""Prevent a structural agent from widening its accepted write surface."""
|
|
275
|
+
if value != KNOWLEDGE_PLANNER_OUTPUT_PATHS:
|
|
276
|
+
raise ValueError("accepted planner output paths are fixed by contract")
|
|
277
|
+
return value
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class KnowledgePlannerSessionRequest(KnowledgeAgentSessionRequest):
|
|
281
|
+
"""One planner or reflection invocation with a decisions-only write surface."""
|
|
282
|
+
|
|
283
|
+
sandbox: KnowledgeAgentSandboxPolicy = Field(
|
|
284
|
+
default_factory=KnowledgePlannerSandboxPolicy
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
@model_validator(mode="after")
|
|
288
|
+
def require_planner_output_contract(self) -> "KnowledgePlannerSessionRequest":
|
|
289
|
+
"""Reject callers that replace the fixed decisions-only output surface."""
|
|
290
|
+
if self.sandbox.accepted_output_paths != KNOWLEDGE_PLANNER_OUTPUT_PATHS:
|
|
291
|
+
raise ValueError("accepted planner output paths are fixed by contract")
|
|
292
|
+
return self
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class KnowledgeOrphanAggregate(BaseModel):
|
|
296
|
+
"""New candidate identities with no compiled-page rule home for one entity."""
|
|
297
|
+
|
|
298
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
299
|
+
|
|
300
|
+
entity_id: UUID
|
|
301
|
+
candidate_keys: tuple[str, ...] = Field(min_length=1)
|
|
302
|
+
|
|
303
|
+
@field_validator("candidate_keys")
|
|
304
|
+
@classmethod
|
|
305
|
+
def canonicalize_candidate_keys(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
306
|
+
"""Count stable candidate identities once per entity."""
|
|
307
|
+
return tuple(sorted(set(value)))
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def candidate_count(self) -> int:
|
|
311
|
+
"""Return the exact number of unique currently unhoused candidates."""
|
|
312
|
+
return len(self.candidate_keys)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class KnowledgePlannerArtifactState(BaseModel):
|
|
316
|
+
"""The structural and health fields a planner may inspect for one page."""
|
|
317
|
+
|
|
318
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
319
|
+
|
|
320
|
+
artifact_id: UUID
|
|
321
|
+
layer: KnowledgeLayer
|
|
322
|
+
page_kind: KnowledgePageKind
|
|
323
|
+
status: KnowledgeArtifactStatus
|
|
324
|
+
git_path: str
|
|
325
|
+
scope_id: UUID | None = None
|
|
326
|
+
parent_artifact_id: UUID | None = None
|
|
327
|
+
artifact_kind: str | None = None
|
|
328
|
+
candidate_count: int = Field(default=0, ge=0)
|
|
329
|
+
uncited_count: int = Field(default=0, ge=0)
|
|
330
|
+
page_size_bytes: int = Field(default=0, ge=0)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class KnowledgeCompiledContentState(BaseModel):
|
|
334
|
+
"""Last accepted body identity used for deterministic drift detection."""
|
|
335
|
+
|
|
336
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
337
|
+
|
|
338
|
+
artifact_id: UUID
|
|
339
|
+
deployment_id: UUID
|
|
340
|
+
git_path: str
|
|
341
|
+
content_hash: str
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class KnowledgePlanningSnapshot(BaseModel):
|
|
345
|
+
"""Bounded structural triggers and health metrics supplied to one agent run."""
|
|
346
|
+
|
|
347
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
348
|
+
|
|
349
|
+
deployment_id: UUID
|
|
350
|
+
scope_id: UUID | None = None
|
|
351
|
+
artifacts: tuple[KnowledgePlannerArtifactState, ...]
|
|
352
|
+
orphan_aggregates: tuple[KnowledgeOrphanAggregate, ...] = ()
|
|
353
|
+
overflow_artifact_ids: CanonicalUUIDs = ()
|
|
354
|
+
community_ids: CanonicalUUIDs = ()
|
|
355
|
+
writer_suggestions: tuple[KnowledgeWriterSuggestion, ...] = ()
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class KnowledgePlanRunWrite(BaseModel):
|
|
359
|
+
"""One terminal transcript-bearing planner or reflection run."""
|
|
360
|
+
|
|
361
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
362
|
+
|
|
363
|
+
run_id: UUID
|
|
364
|
+
deployment_id: UUID
|
|
365
|
+
scope_id: UUID | None = None
|
|
366
|
+
run_kind: KnowledgePlanRunKind
|
|
367
|
+
trigger: KnowledgePlanTrigger
|
|
368
|
+
component_version: str = Field(min_length=1)
|
|
369
|
+
input_hash: str = Field(min_length=1)
|
|
370
|
+
session_transcript_uri: str = Field(min_length=1)
|
|
371
|
+
status: KnowledgePlanRunStatus
|
|
372
|
+
failure: str | None = None
|
|
373
|
+
tokens: int | None = Field(default=None, ge=0)
|
|
374
|
+
cost_usd: Decimal | None = Field(default=None, ge=Decimal("0"))
|
|
375
|
+
|
|
376
|
+
@model_validator(mode="after")
|
|
377
|
+
def require_terminal_failure_shape(self) -> "KnowledgePlanRunWrite":
|
|
378
|
+
"""Keep success and failure rows unambiguous without trimming exceptions."""
|
|
379
|
+
if (self.status is KnowledgePlanRunStatus.FAILED) != (self.failure is not None):
|
|
380
|
+
raise ValueError("only failed plan runs carry a failure traceback")
|
|
381
|
+
return self
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class KnowledgePlanDecisionResult(BaseModel):
|
|
385
|
+
"""The durable consequence assigned to one validated structural proposal."""
|
|
386
|
+
|
|
387
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
388
|
+
|
|
389
|
+
decision_id: UUID
|
|
390
|
+
action: KnowledgePlanAction
|
|
391
|
+
status: KnowledgePlanStatus
|
|
392
|
+
band: KnowledgePlanBand
|
|
393
|
+
blast_radius: int = Field(ge=0)
|
|
394
|
+
expected_impact: Decimal = Field(ge=Decimal("0"))
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class KnowledgePendingPlanDecision(BaseModel):
|
|
398
|
+
"""One accepted structural decision not yet bound to a git revision."""
|
|
399
|
+
|
|
400
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
401
|
+
|
|
402
|
+
decision_id: UUID
|
|
403
|
+
proposal: KnowledgePlanProposal
|
|
404
|
+
decided_at: UTCDateTime
|
|
405
|
+
artifact_paths: dict[UUID, str] = Field(default_factory=dict)
|
|
406
|
+
|
|
407
|
+
@field_validator("artifact_paths")
|
|
408
|
+
@classmethod
|
|
409
|
+
def require_safe_artifact_paths(cls, value: dict[UUID, str]) -> dict[UUID, str]:
|
|
410
|
+
"""Keep reconciled file targets inside the repository namespace."""
|
|
411
|
+
return {
|
|
412
|
+
artifact_id: _normalized_git_path(value=path, field="artifact_paths")
|
|
413
|
+
for artifact_id, path in value.items()
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class KnowledgeQuarantineRecord(BaseModel):
|
|
418
|
+
"""A direct compiled-body edit preserved for explicit triage."""
|
|
419
|
+
|
|
420
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
421
|
+
|
|
422
|
+
quarantine_id: UUID
|
|
423
|
+
decision_id: UUID
|
|
424
|
+
deployment_id: UUID
|
|
425
|
+
artifact_id: UUID
|
|
426
|
+
recorded_content_hash: str
|
|
427
|
+
detected_content_hash: str
|
|
428
|
+
proposed_sidecar_entry: str = Field(min_length=1)
|
|
429
|
+
status: KnowledgeQuarantineStatus
|
|
430
|
+
detected_at: UTCDateTime | None = None
|
|
431
|
+
resolved_at: UTCDateTime | None = None
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Lifecycle values (D54/D55): currency transitions and the reconciliation delta."""
|
|
2
|
+
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from pydantic import ConfigDict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CurrencyTransition(BaseModel):
|
|
10
|
+
"""One testimony-currency ledger event to append (D54).
|
|
11
|
+
|
|
12
|
+
Bookkeeping, never validity: nothing about the claim itself changes.
|
|
13
|
+
``reason`` is a `currency_reason` enum value; the from_* fields name the
|
|
14
|
+
superseded coordinate (generation or version) for the audit trail.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
18
|
+
|
|
19
|
+
claim_id: UUID
|
|
20
|
+
doc_id: UUID
|
|
21
|
+
became_current: bool
|
|
22
|
+
reason: str
|
|
23
|
+
from_extractor_version: str | None = None
|
|
24
|
+
from_version_id: UUID | None = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ReconciliationDelta(BaseModel):
|
|
28
|
+
"""The fact-level outcome of one reconciliation run (lifecycle §5).
|
|
29
|
+
|
|
30
|
+
Fact IDs and outcomes only — never raw claim IDs (the K stale-storm
|
|
31
|
+
guard); this is the `evidence_changed` payload shape.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
35
|
+
|
|
36
|
+
reconciliation_id: UUID
|
|
37
|
+
transitions: int = 0
|
|
38
|
+
recounted_relations: tuple[UUID, ...] = ()
|
|
39
|
+
recounted_observations: tuple[UUID, ...] = ()
|
|
40
|
+
relations_closed: tuple[UUID, ...] = ()
|
|
41
|
+
observations_closed: tuple[UUID, ...] = ()
|
|
42
|
+
flags_raised: tuple[UUID, ...] = ()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Typed, provider-neutral model and embedding call values for the LLM boundary."""
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
from typing import Generic
|
|
6
|
+
from typing import Self
|
|
7
|
+
from typing import TypeAlias
|
|
8
|
+
from typing import TypeVar
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
from pydantic import ConfigDict
|
|
12
|
+
from pydantic import Field
|
|
13
|
+
from pydantic import model_validator
|
|
14
|
+
|
|
15
|
+
_NonEmptyText = Annotated[str, Field(min_length=1)]
|
|
16
|
+
_EmbeddingVector = Annotated[tuple[float, ...], Field(min_length=1)]
|
|
17
|
+
StructuredResponseModel: TypeAlias = BaseModel
|
|
18
|
+
ResponseT = TypeVar("ResponseT", bound=StructuredResponseModel)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ProviderAccountingError(Exception):
|
|
22
|
+
"""A provider response omitted or malformed required usage accounting."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ProviderCallUsage(BaseModel):
|
|
26
|
+
"""Provider-reported accounting for one successful generation or embedding call."""
|
|
27
|
+
|
|
28
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
29
|
+
|
|
30
|
+
model_name: _NonEmptyText
|
|
31
|
+
tokens_in: int = Field(ge=0)
|
|
32
|
+
tokens_out: int = Field(ge=0)
|
|
33
|
+
cost_usd: Decimal = Field(ge=Decimal(0))
|
|
34
|
+
latency_ms: int = Field(ge=0)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class GeneratedResponse(BaseModel, Generic[ResponseT]):
|
|
38
|
+
"""A validated structured output paired with its provider accounting."""
|
|
39
|
+
|
|
40
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
41
|
+
|
|
42
|
+
output: ResponseT
|
|
43
|
+
usage: ProviderCallUsage
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ModelRequest(BaseModel):
|
|
47
|
+
"""A rendered prompt and configured model identifier ready for invocation."""
|
|
48
|
+
|
|
49
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
50
|
+
|
|
51
|
+
model: _NonEmptyText
|
|
52
|
+
prompt: _NonEmptyText
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class EmbeddingRequest(BaseModel):
|
|
56
|
+
"""One non-empty batch for a configured embedding model."""
|
|
57
|
+
|
|
58
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
59
|
+
|
|
60
|
+
model: _NonEmptyText
|
|
61
|
+
texts: Annotated[tuple[_NonEmptyText, ...], Field(min_length=1)]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class EmbeddingResponse(BaseModel):
|
|
65
|
+
"""A non-empty embedding batch whose vectors share one dimension."""
|
|
66
|
+
|
|
67
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
68
|
+
|
|
69
|
+
vectors: Annotated[tuple[_EmbeddingVector, ...], Field(min_length=1)]
|
|
70
|
+
usage: ProviderCallUsage
|
|
71
|
+
|
|
72
|
+
@model_validator(mode="after")
|
|
73
|
+
def require_one_dimension(self) -> Self:
|
|
74
|
+
"""Reject provider responses that mix embedding dimensions."""
|
|
75
|
+
expected_dimension = len(self.vectors[0])
|
|
76
|
+
if any(len(vector) != expected_dimension for vector in self.vectors[1:]):
|
|
77
|
+
raise ValueError("all embedding vectors must have the same dimension")
|
|
78
|
+
return self
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Portable result of publishing the four D51 read-only mount surfaces."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
from typing import Literal
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from pydantic import ConfigDict
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
|
|
11
|
+
_MountLocator = Annotated[str, Field(min_length=1)]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PublishedMounts(BaseModel):
|
|
15
|
+
"""Locations for the exact P3, artifact, raw, and Plane-K read-only views."""
|
|
16
|
+
|
|
17
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
18
|
+
|
|
19
|
+
deployment_id: UUID
|
|
20
|
+
p3: _MountLocator
|
|
21
|
+
artifacts: _MountLocator
|
|
22
|
+
raw: _MountLocator
|
|
23
|
+
knowledge: _MountLocator
|
|
24
|
+
read_only: Literal[True]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Provider-neutral object keys for immutable raw, artifact, and snapshot bytes."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
from pydantic import ConfigDict
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
from pydantic import RootModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ObjectKey(RootModel[Annotated[str, Field(min_length=1)]]):
|
|
11
|
+
"""Opaque non-empty key in the configured immutable object store."""
|
|
12
|
+
|
|
13
|
+
model_config = ConfigDict(frozen=True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ObjectAlreadyExistsError(Exception):
|
|
17
|
+
"""A write to an occupied key — objects are immutable, never replaced."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ObjectKeyEscapesRootError(Exception):
|
|
21
|
+
"""An object key that would resolve outside the store root — refused."""
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Typed results for the WP-7.2 portable operational scale battery."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
from typing import get_args
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from pydantic import ConfigDict
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
from pydantic import model_validator
|
|
11
|
+
|
|
12
|
+
OperationalScaleName = Literal[
|
|
13
|
+
"d23_schema_shape",
|
|
14
|
+
"hub_registry_blocking",
|
|
15
|
+
"hub_lineage_recount",
|
|
16
|
+
"provider_neutral_batching",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
OPERATIONAL_SCALE_NAMES = frozenset(get_args(OperationalScaleName))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OperationalScaleMeasurement(BaseModel):
|
|
23
|
+
"""One fixed-profile measurement and its non-SLA observations."""
|
|
24
|
+
|
|
25
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
26
|
+
|
|
27
|
+
name: OperationalScaleName
|
|
28
|
+
profile: str
|
|
29
|
+
scale: dict[str, Annotated[int, Field(ge=0)]]
|
|
30
|
+
metrics: dict[str, object]
|
|
31
|
+
limitations: tuple[str, ...] = ()
|
|
32
|
+
passed: bool
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class OperationalScaleReport(BaseModel):
|
|
36
|
+
"""The complete four-measurement WP-7.2 report."""
|
|
37
|
+
|
|
38
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
39
|
+
|
|
40
|
+
measurements: Annotated[
|
|
41
|
+
tuple[OperationalScaleMeasurement, ...], Field(min_length=4, max_length=4)
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
@model_validator(mode="after")
|
|
45
|
+
def complete_battery(self) -> "OperationalScaleReport":
|
|
46
|
+
"""Reject duplicate or absent measurements."""
|
|
47
|
+
names = {measurement.name for measurement in self.measurements}
|
|
48
|
+
if names != OPERATIONAL_SCALE_NAMES:
|
|
49
|
+
missing = sorted(OPERATIONAL_SCALE_NAMES - names)
|
|
50
|
+
extra = sorted(names - OPERATIONAL_SCALE_NAMES)
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"operational scale names mismatch: missing={missing}, extra={extra}"
|
|
53
|
+
)
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def passed(self) -> bool:
|
|
58
|
+
"""The report passes only when every structural invariant holds."""
|
|
59
|
+
return all(measurement.passed for measurement in self.measurements)
|