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.
Files changed (186) hide show
  1. rememberstack/__init__.py +9 -0
  2. rememberstack/adapters/__init__.py +42 -0
  3. rememberstack/adapters/codex_writer.py +221 -0
  4. rememberstack/adapters/markitdown_converter.py +42 -0
  5. rememberstack/adapters/openrouter.py +136 -0
  6. rememberstack/adapters/selfhost/__init__.py +54 -0
  7. rememberstack/adapters/selfhost/forget.py +66 -0
  8. rememberstack/adapters/selfhost/git.py +374 -0
  9. rememberstack/adapters/selfhost/lance.py +328 -0
  10. rememberstack/adapters/selfhost/minio.py +279 -0
  11. rememberstack/adapters/selfhost/mounts.py +249 -0
  12. rememberstack/adapters/selfhost/object_store.py +130 -0
  13. rememberstack/adapters/selfhost/projection.py +80 -0
  14. rememberstack/adapters/selfhost/queue.py +137 -0
  15. rememberstack/adapters/selfhost/telemetry.py +45 -0
  16. rememberstack/adapters/selfhost/watcher.py +70 -0
  17. rememberstack/adapters/testing/__init__.py +15 -0
  18. rememberstack/adapters/testing/cost_meter.py +13 -0
  19. rememberstack/adapters/testing/model_provider.py +83 -0
  20. rememberstack/adapters/testing/queue.py +43 -0
  21. rememberstack/adapters/testing/telemetry.py +22 -0
  22. rememberstack/client.py +19 -0
  23. rememberstack/core/__init__.py +127 -0
  24. rememberstack/core/blockizer.py +189 -0
  25. rememberstack/core/chunker.py +216 -0
  26. rememberstack/core/consumption_skill.py +275 -0
  27. rememberstack/core/conversion.py +76 -0
  28. rememberstack/core/core_manifest.py +598 -0
  29. rememberstack/core/extension_packs.py +124 -0
  30. rememberstack/core/forget.py +17 -0
  31. rememberstack/core/knowledge_authored.py +276 -0
  32. rememberstack/core/knowledge_compile.py +215 -0
  33. rememberstack/core/knowledge_fact_sheet.py +210 -0
  34. rememberstack/core/knowledge_hashing.py +68 -0
  35. rememberstack/core/knowledge_planner.py +64 -0
  36. rememberstack/core/knowledge_writer.py +175 -0
  37. rememberstack/core/ranking.py +200 -0
  38. rememberstack/core/recipe_linter.py +149 -0
  39. rememberstack/core/section_snap.py +209 -0
  40. rememberstack/core/storage_routing.py +27 -0
  41. rememberstack/eval/__init__.py +53 -0
  42. rememberstack/eval/consumption.py +141 -0
  43. rememberstack/eval/contradiction.py +184 -0
  44. rememberstack/eval/harness.py +136 -0
  45. rememberstack/eval/lifecycle.py +400 -0
  46. rememberstack/eval/operational_scale.py +49 -0
  47. rememberstack/eval/resolution.py +255 -0
  48. rememberstack/eval/retrieval_spikes.py +50 -0
  49. rememberstack/eval/skeleton.py +231 -0
  50. rememberstack/llm/__init__.py +1 -0
  51. rememberstack/model/__init__.py +589 -0
  52. rememberstack/model/adjudication.py +100 -0
  53. rememberstack/model/auth.py +27 -0
  54. rememberstack/model/blocks.py +30 -0
  55. rememberstack/model/chunks.py +190 -0
  56. rememberstack/model/claims.py +162 -0
  57. rememberstack/model/client.py +98 -0
  58. rememberstack/model/clustering.py +54 -0
  59. rememberstack/model/component_version.py +124 -0
  60. rememberstack/model/consumption.py +88 -0
  61. rememberstack/model/conversion.py +31 -0
  62. rememberstack/model/deployment.py +53 -0
  63. rememberstack/model/documents.py +168 -0
  64. rememberstack/model/envelope.py +513 -0
  65. rememberstack/model/evaluation.py +72 -0
  66. rememberstack/model/forget.py +143 -0
  67. rememberstack/model/git.py +13 -0
  68. rememberstack/model/knowledge.py +840 -0
  69. rememberstack/model/knowledge_authored.py +325 -0
  70. rememberstack/model/knowledge_planner.py +431 -0
  71. rememberstack/model/lifecycle.py +42 -0
  72. rememberstack/model/model_provider.py +78 -0
  73. rememberstack/model/mounts.py +24 -0
  74. rememberstack/model/object_store.py +21 -0
  75. rememberstack/model/operational_scale.py +59 -0
  76. rememberstack/model/operations.py +153 -0
  77. rememberstack/model/processing.py +228 -0
  78. rememberstack/model/queue.py +73 -0
  79. rememberstack/model/recipes.py +83 -0
  80. rememberstack/model/relations.py +79 -0
  81. rememberstack/model/resolution.py +83 -0
  82. rememberstack/model/retrieval_spikes.py +62 -0
  83. rememberstack/model/sections.py +120 -0
  84. rememberstack/model/telemetry.py +30 -0
  85. rememberstack/ports/__init__.py +29 -0
  86. rememberstack/ports/auth.py +16 -0
  87. rememberstack/ports/connector.py +23 -0
  88. rememberstack/ports/cost_meter.py +17 -0
  89. rememberstack/ports/forget.py +20 -0
  90. rememberstack/ports/git.py +20 -0
  91. rememberstack/ports/model_provider.py +28 -0
  92. rememberstack/ports/mounts.py +16 -0
  93. rememberstack/ports/object_store.py +27 -0
  94. rememberstack/ports/p1_index.py +92 -0
  95. rememberstack/ports/purge.py +93 -0
  96. rememberstack/ports/queue.py +23 -0
  97. rememberstack/ports/telemetry.py +21 -0
  98. rememberstack/profiles/__init__.py +22 -0
  99. rememberstack/profiles/selfhost.py +324 -0
  100. rememberstack/profiles/selfhost_forget.py +158 -0
  101. rememberstack/profiles/selfhost_operations.py +95 -0
  102. rememberstack/py.typed +1 -0
  103. rememberstack/spine/__init__.py +93 -0
  104. rememberstack/spine/admission.py +26 -0
  105. rememberstack/spine/backfill.py +168 -0
  106. rememberstack/spine/catalog_contract.py +742 -0
  107. rememberstack/spine/chunk_catalog.py +237 -0
  108. rememberstack/spine/claim_catalog.py +298 -0
  109. rememberstack/spine/clustering.py +740 -0
  110. rememberstack/spine/component_versions.py +208 -0
  111. rememberstack/spine/consumption.py +81 -0
  112. rememberstack/spine/deployment_bootstrap.py +445 -0
  113. rememberstack/spine/document_catalog.py +621 -0
  114. rememberstack/spine/entity_registry.py +205 -0
  115. rememberstack/spine/extension_packs.py +220 -0
  116. rememberstack/spine/fact_catalog.py +571 -0
  117. rememberstack/spine/forget.py +1753 -0
  118. rememberstack/spine/knowledge.py +5467 -0
  119. rememberstack/spine/lifecycle.py +1071 -0
  120. rememberstack/spine/migrations/__init__.py +1 -0
  121. rememberstack/spine/migrations/_helpers.py +153 -0
  122. rememberstack/spine/migrations/env.py +58 -0
  123. rememberstack/spine/migrations/script.py.mako +27 -0
  124. rememberstack/spine/migrations/versions/__init__.py +1 -0
  125. rememberstack/spine/migrations/versions/p0_02_0001_extensions_enums.py +189 -0
  126. rememberstack/spine/migrations/versions/p0_02_0002_infrastructure_registries.py +321 -0
  127. rememberstack/spine/migrations/versions/p0_02_0003_entities_evaluation_e0_e1.py +631 -0
  128. rememberstack/spine/migrations/versions/p0_02_0004_claims_facts_evidence.py +411 -0
  129. rememberstack/spine/migrations/versions/p0_02_0005_projection_knowledge_retrieval.py +391 -0
  130. rememberstack/spine/migrations/versions/p0_02_0006_partitions_views.py +158 -0
  131. rememberstack/spine/migrations/versions/p2_06_0007_invalidated_outcome.py +26 -0
  132. rememberstack/spine/migrations/versions/p3_01_0008_document_version_target.py +58 -0
  133. rememberstack/spine/migrations/versions/p3_05_0009_reconcile_stage.py +27 -0
  134. rememberstack/spine/migrations/versions/p3_07_0010_lifecycle_eval_suite.py +25 -0
  135. rememberstack/spine/migrations/versions/p4_01_0011_survivor_view_rewrite.py +57 -0
  136. rememberstack/spine/migrations/versions/p6_02_0012_knowledge_compile_recovery.py +58 -0
  137. rememberstack/spine/migrations/versions/p6_04_0013_knowledge_writer_ledger.py +46 -0
  138. rememberstack/spine/migrations/versions/p6_05_0014_knowledge_planner_runtime.py +217 -0
  139. rememberstack/spine/migrations/versions/p6_06_0015_authored_dispatch_runtime.py +38 -0
  140. rememberstack/spine/migrations/versions/p7_02_0016_operational_eval_suite.py +19 -0
  141. rememberstack/spine/migrations/versions/p7_05_0017_hard_forget.py +55 -0
  142. rememberstack/spine/observation_adjudication.py +778 -0
  143. rememberstack/spine/operations.py +298 -0
  144. rememberstack/spine/projection.py +662 -0
  145. rememberstack/spine/recipes.py +276 -0
  146. rememberstack/spine/resolver.py +763 -0
  147. rememberstack/spine/review.py +650 -0
  148. rememberstack/spine/settings.py +22 -0
  149. rememberstack/spine/supersession.py +510 -0
  150. rememberstack/spine/sync.py +128 -0
  151. rememberstack/spine/work_ledger.py +816 -0
  152. rememberstack/surfaces/__init__.py +110 -0
  153. rememberstack/surfaces/cli.py +447 -0
  154. rememberstack/surfaces/consumption_skill.py +87 -0
  155. rememberstack/surfaces/graph_queries.py +698 -0
  156. rememberstack/surfaces/http_api.py +377 -0
  157. rememberstack/surfaces/mcp.py +67 -0
  158. rememberstack/surfaces/query_engine.py +1591 -0
  159. rememberstack/surfaces/recipe_executor.py +185 -0
  160. rememberstack/surfaces/recipe_surface.py +219 -0
  161. rememberstack/surfaces/remote_mcp.py +133 -0
  162. rememberstack/surfaces/sdk.py +324 -0
  163. rememberstack/workers/__init__.py +155 -0
  164. rememberstack/workers/base.py +312 -0
  165. rememberstack/workers/e0.py +577 -0
  166. rememberstack/workers/e1.py +425 -0
  167. rememberstack/workers/e2.py +525 -0
  168. rememberstack/workers/e3.py +434 -0
  169. rememberstack/workers/forget.py +299 -0
  170. rememberstack/workers/knowledge_authored.py +146 -0
  171. rememberstack/workers/knowledge_driver.py +735 -0
  172. rememberstack/workers/knowledge_fact_sheet.py +123 -0
  173. rememberstack/workers/knowledge_planner.py +325 -0
  174. rememberstack/workers/knowledge_writer.py +393 -0
  175. rememberstack/workers/operations.py +42 -0
  176. rememberstack/workers/p1.py +234 -0
  177. rememberstack/workers/p2.py +513 -0
  178. rememberstack/workers/p2_analytics.py +276 -0
  179. rememberstack/workers/p3.py +673 -0
  180. rememberstack/workers/reconcile.py +485 -0
  181. rememberstack/workers/sync.py +168 -0
  182. rememberstack-0.1.0.dist-info/METADATA +213 -0
  183. rememberstack-0.1.0.dist-info/RECORD +186 -0
  184. rememberstack-0.1.0.dist-info/WHEEL +4 -0
  185. rememberstack-0.1.0.dist-info/entry_points.txt +2 -0
  186. rememberstack-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,27 @@
1
+ """Provider-neutral values for the D50/D60 single-deployment auth perimeter."""
2
+
3
+ from typing import Annotated
4
+ from uuid import UUID
5
+
6
+ from pydantic import BaseModel
7
+ from pydantic import ConfigDict
8
+ from pydantic import Field
9
+ from pydantic import SecretBytes
10
+
11
+
12
+ class PerimeterCredential(BaseModel):
13
+ """Opaque perimeter credential passed to the configured auth adapter."""
14
+
15
+ model_config = ConfigDict(frozen=True, extra="forbid")
16
+
17
+ scheme: Annotated[str, Field(min_length=1)]
18
+ value: SecretBytes
19
+
20
+
21
+ class AuthenticatedContext(BaseModel):
22
+ """Authenticated principal inside one deployment-wide trust domain."""
23
+
24
+ model_config = ConfigDict(frozen=True, extra="forbid")
25
+
26
+ deployment_id: UUID
27
+ principal: Annotated[str, Field(min_length=1)]
@@ -0,0 +1,30 @@
1
+ """Typed block records — the deterministic identity atoms of a document (D57)."""
2
+
3
+ from enum import StrEnum
4
+
5
+ from pydantic import BaseModel
6
+ from pydantic import ConfigDict
7
+ from pydantic import Field
8
+
9
+
10
+ class BlockType(StrEnum):
11
+ """The structural kinds a blockizer emits (e1 §2)."""
12
+
13
+ PARAGRAPH = "paragraph"
14
+ HEADING = "heading"
15
+ TABLE = "table"
16
+ LIST_ITEM = "list_item"
17
+ CODE = "code"
18
+ QUOTE = "quote"
19
+
20
+
21
+ class Block(BaseModel):
22
+ """One block: a slice of document.md with its deterministic identity hash."""
23
+
24
+ model_config = ConfigDict(frozen=True, extra="forbid")
25
+
26
+ ordinal: int = Field(ge=0)
27
+ type: BlockType
28
+ char_start: int = Field(ge=0)
29
+ char_end: int = Field(ge=0)
30
+ block_hash: str
@@ -0,0 +1,190 @@
1
+ """E1 chunk-layer values: packing inputs/outputs, catalog records, P1 rows (D58).
2
+
3
+ Chunks are non-overlapping runs of whole blocks within one section; their
4
+ identity is the ordered block-hash sequence, which is what makes reuse (D56)
5
+ a sequence comparison instead of a semantic judgment.
6
+ """
7
+
8
+ from typing import Annotated
9
+ from uuid import UUID
10
+
11
+ from pydantic import BaseModel
12
+ from pydantic import ConfigDict
13
+ from pydantic import Field
14
+
15
+ from rememberstack.model.queue import UTCDateTime
16
+
17
+ _NonEmpty = Annotated[str, Field(min_length=1)]
18
+
19
+
20
+ class SectionSpan(BaseModel):
21
+ """One section's block range and signals, as the chunker consumes it."""
22
+
23
+ model_config = ConfigDict(frozen=True, extra="forbid")
24
+
25
+ section_id: UUID
26
+ node_path: str
27
+ role: str
28
+ block_start: int = Field(ge=0)
29
+ block_end: int = Field(ge=-1)
30
+
31
+
32
+ class ChunkSource(BaseModel):
33
+ """Everything the chunk stage loads about its claimed representation."""
34
+
35
+ model_config = ConfigDict(frozen=True, extra="forbid")
36
+
37
+ deployment_id: UUID
38
+ doc_id: UUID
39
+ version_id: UUID
40
+ representation_id: UUID
41
+ markdown_uri: str
42
+ blocks_uri: str
43
+ title: str | None
44
+ source_kind: str
45
+ source_modified_at: UTCDateTime | None
46
+ published_at: UTCDateTime | None
47
+ language: str | None
48
+ structurer_version: str
49
+ sections: tuple[SectionSpan, ...]
50
+
51
+
52
+ class PackedChunk(BaseModel):
53
+ """One packed run of whole blocks: the chunker's pure output."""
54
+
55
+ model_config = ConfigDict(frozen=True, extra="forbid")
56
+
57
+ ordinal: int = Field(ge=0)
58
+ section_id: UUID
59
+ block_start: int = Field(ge=0)
60
+ block_end: int = Field(ge=0)
61
+ char_start: int = Field(ge=0)
62
+ char_end: int = Field(ge=0)
63
+ chunk_content_hash: _NonEmpty
64
+ token_count: int = Field(ge=0)
65
+
66
+
67
+ class ChunkRecord(BaseModel):
68
+ """One chunk row for the spine ledger (text and vectors live elsewhere, D37/D8)."""
69
+
70
+ model_config = ConfigDict(frozen=True, extra="forbid")
71
+
72
+ chunk_id: UUID
73
+ deployment_id: UUID
74
+ doc_id: UUID
75
+ version_id: UUID
76
+ representation_id: UUID
77
+ section_id: UUID
78
+ ordinal: int = Field(ge=0)
79
+ block_start: int = Field(ge=0)
80
+ block_end: int = Field(ge=0)
81
+ chunk_content_hash: _NonEmpty
82
+ extraction_input_hash: _NonEmpty
83
+ char_start: int = Field(ge=0)
84
+ char_end: int = Field(ge=0)
85
+ token_count: int = Field(ge=0)
86
+ chunker_version: _NonEmpty
87
+
88
+
89
+ class ChunkForEmbedding(BaseModel):
90
+ """One chunk row as the embed stage loads it (spans + signals, no body)."""
91
+
92
+ model_config = ConfigDict(frozen=True, extra="forbid")
93
+
94
+ chunk_id: UUID
95
+ doc_id: UUID
96
+ version_id: UUID
97
+ ordinal: int = Field(ge=0)
98
+ char_start: int = Field(ge=0)
99
+ char_end: int = Field(ge=0)
100
+ chunk_content_hash: str
101
+ extraction_input_hash: str
102
+ section_role: str
103
+ section_path: str
104
+ context_prefix: str | None
105
+ prefixer_version: str | None
106
+
107
+
108
+ class CarryForwardSource(BaseModel):
109
+ """A prior version's chunk whose LLM-derived context is carried forward.
110
+
111
+ D56/A3: for an unchanged chunk (same content hash within the lineage) the
112
+ stored prefix and embedding are REUSED, never regenerated — LLM output is
113
+ non-deterministic, so regenerating would both pay again and produce
114
+ different bytes.
115
+ """
116
+
117
+ model_config = ConfigDict(frozen=True, extra="forbid")
118
+
119
+ chunk_id: UUID
120
+ context_prefix: str
121
+
122
+
123
+ class EmbeddingUpdate(BaseModel):
124
+ """The embed stage's write-back onto one chunk row."""
125
+
126
+ model_config = ConfigDict(frozen=True, extra="forbid")
127
+
128
+ chunk_id: UUID
129
+ embedding_ref: _NonEmpty
130
+ embedding_version: _NonEmpty
131
+ context_prefix: _NonEmpty
132
+ prefixer_version: _NonEmpty
133
+
134
+
135
+ class ContextPrefix(BaseModel):
136
+ """The structured response of the E1 context-prefix call (D58/D63)."""
137
+
138
+ model_config = ConfigDict(frozen=True, extra="forbid")
139
+
140
+ prefix: _NonEmpty
141
+
142
+
143
+ class P1ChunkRow(BaseModel):
144
+ """One row of the P1 chunk table: text + vector + filter scalars (D8)."""
145
+
146
+ model_config = ConfigDict(frozen=True, extra="forbid")
147
+
148
+ chunk_id: UUID
149
+ deployment_id: UUID
150
+ doc_id: UUID
151
+ version_id: UUID
152
+ section_role: str
153
+ text: _NonEmpty
154
+ vector: Annotated[tuple[float, ...], Field(min_length=1)]
155
+
156
+
157
+ class ChunkSourceNotFoundError(Exception):
158
+ """The chunk stage referenced a representation the spine does not know."""
159
+
160
+
161
+ class P1ClaimRow(BaseModel):
162
+ """One row of the P1 claims channel: the needle index (D8/D58).
163
+
164
+ `is_current_testimony` is the scalar the DEFAULT claims channel filters
165
+ on (retrieval §5): current-testimony-only unless a recipe asks otherwise.
166
+ """
167
+
168
+ model_config = ConfigDict(frozen=True, extra="forbid")
169
+
170
+ claim_id: UUID
171
+ deployment_id: UUID
172
+ doc_id: UUID
173
+ chunk_id: UUID
174
+ text: Annotated[str, Field(min_length=1)]
175
+ is_current_testimony: bool
176
+ is_attributed: bool
177
+ vector: Annotated[tuple[float, ...], Field(min_length=1)]
178
+
179
+
180
+ class P1FactRow(BaseModel):
181
+ """One row of the P1 facts channel: fact/observation labels (D8)."""
182
+
183
+ model_config = ConfigDict(frozen=True, extra="forbid")
184
+
185
+ fact_id: UUID
186
+ deployment_id: UUID
187
+ kind: Annotated[str, Field(min_length=1)] # relation | observation
188
+ label: Annotated[str, Field(min_length=1)]
189
+ status: Annotated[str, Field(min_length=1)] # active | invalidated
190
+ vector: Annotated[tuple[float, ...], Field(min_length=1)]
@@ -0,0 +1,162 @@
1
+ """E2 claim-extraction values: call responses, grounding, and ledger records (D31-D35)."""
2
+
3
+ from enum import StrEnum
4
+ from typing import Annotated
5
+ from uuid import UUID
6
+
7
+ from pydantic import BaseModel
8
+ from pydantic import ConfigDict
9
+ from pydantic import Field
10
+
11
+ _NonEmpty = Annotated[str, Field(min_length=1)]
12
+
13
+
14
+ class SelectionVerdict(StrEnum):
15
+ """Selection's per-candidate outcome (D31/D35)."""
16
+
17
+ KEEP = "keep"
18
+ KEEP_FLAGGED = "keep_flagged"
19
+ DROP = "drop"
20
+
21
+
22
+ class SelectionCandidate(BaseModel):
23
+ """One proposition Selection judged inside the target chunk."""
24
+
25
+ model_config = ConfigDict(frozen=True, extra="forbid")
26
+
27
+ source_span: _NonEmpty
28
+ verdict: SelectionVerdict
29
+ drop_reason: str | None = None
30
+ protected_class: str | None = None
31
+
32
+
33
+ class SelectionResponse(BaseModel):
34
+ """The Selection call's structured output: every judged candidate."""
35
+
36
+ model_config = ConfigDict(frozen=True, extra="forbid")
37
+
38
+ candidates: tuple[SelectionCandidate, ...]
39
+
40
+
41
+ class AddedContext(BaseModel):
42
+ """One substring decontextualization added, tagged with its bundle source (D32)."""
43
+
44
+ model_config = ConfigDict(frozen=True, extra="forbid")
45
+
46
+ text: _NonEmpty
47
+ source_kind: _NonEmpty # header | neighbour | prefix | hint
48
+ source_ref: str | None = None
49
+
50
+
51
+ class CandidateClaim(BaseModel):
52
+ """One decontextualized, decomposed claim before the deterministic gate."""
53
+
54
+ model_config = ConfigDict(frozen=True, extra="forbid")
55
+
56
+ claim_text: _NonEmpty
57
+ source_span: _NonEmpty
58
+ added_context: tuple[AddedContext, ...] = ()
59
+ entailment_self_verdict: bool
60
+ is_attributed: bool = False
61
+
62
+
63
+ class ClaimifyResponse(BaseModel):
64
+ """The fused call's structured output: decontextualize + decompose + ground."""
65
+
66
+ model_config = ConfigDict(frozen=True, extra="forbid")
67
+
68
+ claims: tuple[CandidateClaim, ...]
69
+
70
+
71
+ class ClaimRecord(BaseModel):
72
+ """One accepted claim row: past the deterministic grounding gate (D32)."""
73
+
74
+ model_config = ConfigDict(frozen=True, extra="forbid")
75
+
76
+ claim_id: UUID
77
+ deployment_id: UUID
78
+ doc_id: UUID
79
+ chunk_id: UUID
80
+ section_id: UUID | None
81
+ claim_text: _NonEmpty
82
+ source_span: _NonEmpty
83
+ char_start: int = Field(ge=0)
84
+ char_end: int = Field(ge=0)
85
+ added_context: tuple[AddedContext, ...]
86
+ is_attributed: bool
87
+ entailment_self_verdict: bool
88
+ kept_flagged: bool
89
+ extractor_version: _NonEmpty
90
+
91
+
92
+ class DecisionType(StrEnum):
93
+ """The D33 ledger's decision kinds."""
94
+
95
+ SELECTION_DROP = "selection_drop"
96
+ SELECTION_KEEP_FLAGGED = "selection_keep_flagged"
97
+ DECONTEXT_EDIT = "decontext_edit"
98
+
99
+
100
+ class DecisionRecord(BaseModel):
101
+ """One append-only extraction-transcript row (D33)."""
102
+
103
+ model_config = ConfigDict(frozen=True, extra="forbid")
104
+
105
+ decision_id: UUID
106
+ deployment_id: UUID
107
+ doc_id: UUID
108
+ chunk_id: UUID
109
+ claim_id: UUID | None
110
+ decision_type: DecisionType
111
+ source_span: str | None
112
+ reason: str | None
113
+ edit_detail: dict[str, object] | None
114
+ protected_class: str | None
115
+ extractor_version: _NonEmpty
116
+
117
+
118
+ class ClaimForEmbedding(BaseModel):
119
+ """One claim row as the claim-embed stage loads it."""
120
+
121
+ model_config = ConfigDict(frozen=True, extra="forbid")
122
+
123
+ claim_id: UUID
124
+ doc_id: UUID
125
+ chunk_id: UUID
126
+ claim_text: _NonEmpty
127
+ is_current_testimony: bool
128
+ is_attributed: bool
129
+
130
+
131
+ class FactForLabeling(BaseModel):
132
+ """One relation as the label stage loads it (names resolved for the label)."""
133
+
134
+ model_config = ConfigDict(frozen=True, extra="forbid")
135
+
136
+ relation_id: UUID
137
+ subject_name: _NonEmpty
138
+ predicate: _NonEmpty
139
+ object_name: _NonEmpty
140
+ status: _NonEmpty
141
+
142
+
143
+ class ObservationForEmbedding(BaseModel):
144
+ """One observation as the label stage loads it (obs_label is the text)."""
145
+
146
+ model_config = ConfigDict(frozen=True, extra="forbid")
147
+
148
+ observation_id: UUID
149
+ obs_label: _NonEmpty
150
+ status: _NonEmpty
151
+
152
+
153
+ class FactLabelResponse(BaseModel):
154
+ """The fact-labeler call's structured output: one readable sentence."""
155
+
156
+ model_config = ConfigDict(frozen=True, extra="forbid")
157
+
158
+ label: _NonEmpty
159
+
160
+
161
+ class OtherPredicateGrammarError(Exception):
162
+ """An other:<freetext> escape value violating the D5 grammar."""
@@ -0,0 +1,98 @@
1
+ """Client-surface values that are safe in the dependency-light base install."""
2
+
3
+ from typing import Literal
4
+ from typing import Self
5
+ from uuid import UUID
6
+
7
+ from pydantic import BaseModel
8
+ from pydantic import ConfigDict
9
+ from pydantic import Field
10
+ from pydantic import JsonValue
11
+ from pydantic import model_validator
12
+
13
+ _SECRET_CONFIGURATION_KEYS = frozenset(
14
+ {
15
+ "accesstoken",
16
+ "apikey",
17
+ "credential",
18
+ "credentials",
19
+ "password",
20
+ "refreshtoken",
21
+ "secret",
22
+ "token",
23
+ }
24
+ )
25
+
26
+
27
+ class ConnectorNotFoundError(Exception):
28
+ """A connector id is not present in this deployment."""
29
+
30
+
31
+ class ToolDescriptor(BaseModel):
32
+ """One deployment recipe rendered as a typed SDK/MCP tool."""
33
+
34
+ model_config = ConfigDict(frozen=True, extra="forbid")
35
+
36
+ name: str
37
+ description: str
38
+ input_schema: dict[str, object]
39
+ output_grain: str
40
+ answer_intent: str
41
+
42
+
43
+ class ConnectorCreate(BaseModel):
44
+ """Deployment-side connector configuration sent by a client.
45
+
46
+ ``credential_ref`` names a secret already held by the deployment. Raw
47
+ credentials never become client-surface configuration.
48
+ """
49
+
50
+ model_config = ConfigDict(frozen=True, extra="forbid")
51
+
52
+ kind: str = Field(min_length=1)
53
+ name: str = Field(min_length=1)
54
+ configuration: dict[str, JsonValue] = Field(default_factory=dict)
55
+ credential_ref: str | None = None
56
+
57
+ @model_validator(mode="after")
58
+ def _credentials_are_references(self) -> Self:
59
+ """Reject conventional secret fields at any configuration depth."""
60
+ secret_key = _find_secret_key(self.configuration)
61
+ if secret_key is not None:
62
+ raise ValueError(
63
+ f"configuration field {secret_key!r} looks like a credential;"
64
+ " store it deployment-side and use credential_ref"
65
+ )
66
+ return self
67
+
68
+
69
+ class ConnectorDescriptor(BaseModel):
70
+ """One managed connector, never an instruction to execute it client-side."""
71
+
72
+ model_config = ConfigDict(frozen=True, extra="forbid")
73
+
74
+ connector_id: UUID
75
+ kind: str
76
+ name: str
77
+ status: Literal["active", "paused", "error"]
78
+ configuration: dict[str, JsonValue] = Field(default_factory=dict)
79
+ credential_ref: str | None = None
80
+ message: str | None = None
81
+
82
+
83
+ def _find_secret_key(value: object) -> str | None:
84
+ """Return the first conventional credential key in nested JSON-like data."""
85
+ if isinstance(value, dict):
86
+ for key, nested in value.items():
87
+ normalized = key.casefold().replace("-", "").replace("_", "")
88
+ if normalized in _SECRET_CONFIGURATION_KEYS:
89
+ return key
90
+ found = _find_secret_key(nested)
91
+ if found is not None:
92
+ return found
93
+ elif isinstance(value, (list, tuple)):
94
+ for nested in value:
95
+ found = _find_secret_key(nested)
96
+ if found is not None:
97
+ return found
98
+ return None
@@ -0,0 +1,54 @@
1
+ """Clustering values (D21): config, merge outcomes, and neighborhood reports.
2
+
3
+ The asymmetry every number serves: over-merging is catastrophic and silent;
4
+ under-merging is gradual and recoverable — so the machinery is paranoid in
5
+ one direction, and every merge is reversible.
6
+ """
7
+
8
+ from typing import Annotated
9
+ from uuid import UUID
10
+
11
+ from pydantic import BaseModel
12
+ from pydantic import ConfigDict
13
+ from pydantic import Field
14
+
15
+
16
+ class ClusterConfig(BaseModel):
17
+ """The decide-stage parameters (starting points to measure, D22).
18
+
19
+ `distance_cut` is the HAC cut on cosine DISTANCE (1 - similarity):
20
+ pieces below the cut are one entity. `blob_cap` is the black-hole
21
+ guard; `blast_radius_cap` routes big merges to human review (D24).
22
+ """
23
+
24
+ model_config = ConfigDict(frozen=True, extra="forbid")
25
+
26
+ distance_cut: Annotated[float, Field(ge=0.0, le=2.0)] = 0.15
27
+ blob_cap: Annotated[int, Field(ge=2)] = 50
28
+ blast_radius_cap: Annotated[int, Field(ge=1)] = 100
29
+
30
+
31
+ class MergeProposal(BaseModel):
32
+ """One decide-stage grouping: entities the cut placed in one piece."""
33
+
34
+ model_config = ConfigDict(frozen=True, extra="forbid")
35
+
36
+ survivor_id: UUID
37
+ absorbed_ids: tuple[UUID, ...]
38
+ blast_radius: int = Field(ge=0)
39
+ mean_distance: float
40
+
41
+
42
+ class NeighborhoodReport(BaseModel):
43
+ """What one neighborhood re-decision did (nDR, registries §6)."""
44
+
45
+ model_config = ConfigDict(frozen=True, extra="forbid")
46
+
47
+ members: int
48
+ merged: tuple[UUID, ...] = () # merge_event ids written
49
+ queued_for_review: int = 0
50
+ black_hole_tightened: bool = False
51
+
52
+
53
+ class UnmergeError(Exception):
54
+ """The merge event cannot be reversed (already reversed, or unknown)."""
@@ -0,0 +1,124 @@
1
+ """Typed values and errors for immutable pipeline component versions."""
2
+
3
+ from collections.abc import Mapping
4
+ from datetime import datetime
5
+ from enum import StrEnum
6
+ import re
7
+ from typing import Annotated
8
+ from typing import Any
9
+ from typing import Self
10
+ from typing import TypeAlias
11
+ from uuid import UUID
12
+
13
+ from pydantic import BaseModel
14
+ from pydantic import ConfigDict
15
+ from pydantic import Field
16
+ from pydantic import field_validator
17
+ from pydantic import model_validator
18
+ from pydantic import StringConstraints
19
+
20
+ NonEmptyStrictString: TypeAlias = Annotated[
21
+ str, StringConstraints(strict=True, min_length=1)
22
+ ]
23
+
24
+
25
+ class PipelineComponent(StrEnum):
26
+ """Exact values of the binding Postgres ``pipeline_component`` enum."""
27
+
28
+ INGESTER = "ingester"
29
+ CONVERTER = "converter"
30
+ BLOCKIZER = "blockizer"
31
+ STRUCTURER = "structurer"
32
+ CROSSREFERENCER = "crossreferencer"
33
+ CHUNKER = "chunker"
34
+ CONTEXT_PREFIXER = "context_prefixer"
35
+ EXTRACTOR = "extractor"
36
+ GROUNDER = "grounder"
37
+ RESOLVER = "resolver"
38
+ NORMALIZER = "normalizer"
39
+ ADJUDICATOR = "adjudicator"
40
+ EMBEDDER = "embedder"
41
+ FACT_LABELER = "fact_labeler"
42
+ PROFILE_SUMMARIZER = "profile_summarizer"
43
+ COMMUNITY_DETECTOR = "community_detector"
44
+ SNAPSHOT_BUILDER = "snapshot_builder"
45
+ KNOWLEDGE_PLANNER = "knowledge_planner"
46
+ KNOWLEDGE_WRITER = "knowledge_writer"
47
+ KNOWLEDGE_REFLECTOR = "knowledge_reflector"
48
+ KNOWLEDGE_LINTER = "knowledge_linter"
49
+ JUDGE = "judge"
50
+ FORGETTER = "forgetter"
51
+
52
+
53
+ class RegisterComponentVersionInput(BaseModel):
54
+ """One explicit immutable pipeline component definition to register."""
55
+
56
+ model_config = ConfigDict(frozen=True, extra="forbid")
57
+
58
+ deployment_id: UUID
59
+ component: PipelineComponent
60
+ version: NonEmptyStrictString
61
+ model_name: str | None = None
62
+ prompt_hash: str | None = None
63
+ embedding_dim: int | None = None
64
+ params: Mapping[str, Any] = Field(default_factory=dict)
65
+ notes: str | None = None
66
+
67
+ @field_validator("prompt_hash")
68
+ @classmethod
69
+ def require_lowercase_sha256(cls, value: str | None) -> str | None:
70
+ """Require an exact lowercase hexadecimal SHA-256 when one is supplied."""
71
+ if value is not None and re.fullmatch(r"[0-9a-f]{64}", value) is None:
72
+ raise ValueError(
73
+ "prompt_hash must be exactly 64 lowercase hexadecimal characters"
74
+ )
75
+ return value
76
+
77
+ @model_validator(mode="after")
78
+ def require_embedder_for_embedding_dimension(self) -> Self:
79
+ """Allow an embedding dimension only on the embedder component."""
80
+ if (
81
+ self.embedding_dim is not None
82
+ and self.component is not PipelineComponent.EMBEDDER
83
+ ):
84
+ raise ValueError("embedding_dim is allowed only for the embedder component")
85
+ return self
86
+
87
+
88
+ class RegisterComponentVersionResult(BaseModel):
89
+ """Identity and creation disposition for one registration attempt."""
90
+
91
+ model_config = ConfigDict(frozen=True, extra="forbid")
92
+
93
+ deployment_id: UUID
94
+ component: PipelineComponent
95
+ version: str
96
+ created: bool
97
+
98
+
99
+ class ComponentVersionRecord(BaseModel):
100
+ """A resolved immutable component definition including its database timestamp."""
101
+
102
+ model_config = ConfigDict(frozen=True, extra="forbid")
103
+
104
+ deployment_id: UUID
105
+ component: PipelineComponent
106
+ version: str
107
+ model_name: str | None
108
+ prompt_hash: str | None
109
+ embedding_dim: int | None
110
+ params: Mapping[str, Any]
111
+ notes: str | None
112
+ configured_at: datetime
113
+
114
+
115
+ class ComponentVersionError(RuntimeError):
116
+ """Base class for a known component-version catalog failure."""
117
+
118
+
119
+ class ComponentVersionConflictError(ComponentVersionError):
120
+ """An existing key has a different immutable component definition."""
121
+
122
+
123
+ class ComponentVersionNotFoundError(ComponentVersionError):
124
+ """No component version exists for the requested primary-key triple."""