clm-kernel 0.0.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 (333) hide show
  1. clm/__init__.py +230 -0
  2. clm/__main__.py +25 -0
  3. clm/cross_cutting/__init__.py +50 -0
  4. clm/cross_cutting/_test_runner.py +36 -0
  5. clm/cross_cutting/async_utils.py +25 -0
  6. clm/cross_cutting/cel_builder.py +123 -0
  7. clm/cross_cutting/cel_converter.py +697 -0
  8. clm/cross_cutting/cel_engine.py +252 -0
  9. clm/cross_cutting/cel_ops.py +72 -0
  10. clm/cross_cutting/cel_ops_db.py +157 -0
  11. clm/cross_cutting/cel_ops_files.py +99 -0
  12. clm/cross_cutting/cel_ops_state.py +71 -0
  13. clm/cross_cutting/cel_ops_strings.py +64 -0
  14. clm/cross_cutting/cel_resolver.py +63 -0
  15. clm/cross_cutting/cel_slices.py +56 -0
  16. clm/cross_cutting/cel_store.py +330 -0
  17. clm/cross_cutting/clm_logger.py +140 -0
  18. clm/cross_cutting/config/__init__.py +155 -0
  19. clm/cross_cutting/config/config_constants.py +392 -0
  20. clm/cross_cutting/config/env_parameters.py +90 -0
  21. clm/cross_cutting/config/logging.py +378 -0
  22. clm/cross_cutting/config/settings.py +504 -0
  23. clm/cross_cutting/context.py +5 -0
  24. clm/cross_cutting/domain_models.py +55 -0
  25. clm/cross_cutting/domain_types.py +61 -0
  26. clm/cross_cutting/effects.py +67 -0
  27. clm/cross_cutting/errors.py +153 -0
  28. clm/cross_cutting/governance.py +331 -0
  29. clm/cross_cutting/guards.py +41 -0
  30. clm/cross_cutting/mcard.py +321 -0
  31. clm/cross_cutting/native.py +185 -0
  32. clm/cross_cutting/observability.py +289 -0
  33. clm/cross_cutting/pcard.py +434 -0
  34. clm/cross_cutting/registry.py +71 -0
  35. clm/cross_cutting/resources.py +84 -0
  36. clm/cross_cutting/secrets.py +44 -0
  37. clm/cross_cutting/subprocess_utils.py +118 -0
  38. clm/cross_cutting/telemetry.py +164 -0
  39. clm/cross_cutting/testing/__init__.py +8 -0
  40. clm/cross_cutting/testing/comparator.py +187 -0
  41. clm/cross_cutting/testing/datasets.py +87 -0
  42. clm/cross_cutting/timeutil.py +20 -0
  43. clm/cross_cutting/types/new_type_check.py +0 -0
  44. clm/cross_cutting/utils/__init__.py +1 -0
  45. clm/cross_cutting/utils/url_safety.py +50 -0
  46. clm/cross_cutting/vcard.py +560 -0
  47. clm/cross_cutting/writer.py +118 -0
  48. clm/layer0/README.md +50 -0
  49. clm/layer0/__init__.py +223 -0
  50. clm/layer0/algebra.py +60 -0
  51. clm/layer0/algebra_baldwin.py +133 -0
  52. clm/layer0/algebra_baldwin_generators.py +36 -0
  53. clm/layer0/algebra_equivalence.py +262 -0
  54. clm/layer0/algebra_ingest.py +306 -0
  55. clm/layer0/algebra_manifest_io.py +156 -0
  56. clm/layer0/assembler.py +481 -0
  57. clm/layer0/cel/__init__.py +23 -0
  58. clm/layer0/cel_eval.py +491 -0
  59. clm/layer0/codec.py +106 -0
  60. clm/layer0/context.py +122 -0
  61. clm/layer0/db.py +160 -0
  62. clm/layer0/exceptions.py +73 -0
  63. clm/layer0/fibration.py +106 -0
  64. clm/layer0/gates.py +50 -0
  65. clm/layer0/hash.py +80 -0
  66. clm/layer0/loader.py +826 -0
  67. clm/layer0/math_utils.py +163 -0
  68. clm/layer0/mcard.py +96 -0
  69. clm/layer0/model/__init__.py +0 -0
  70. clm/layer0/model/action.py +223 -0
  71. clm/layer0/model/card.py +280 -0
  72. clm/layer0/model/card_triad.py +427 -0
  73. clm/layer0/model/compatibility.py +60 -0
  74. clm/layer0/model/dictionary.py +46 -0
  75. clm/layer0/model/dots.py +331 -0
  76. clm/layer0/model/event_producer.py +88 -0
  77. clm/layer0/model/g_time.py +89 -0
  78. clm/layer0/model/handle.py +163 -0
  79. clm/layer0/model/hash/__init__.py +6 -0
  80. clm/layer0/model/hash/algorithms/__init__.py +3 -0
  81. clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
  82. clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
  83. clm/layer0/model/hash/constants.py +53 -0
  84. clm/layer0/model/hash/enums.py +60 -0
  85. clm/layer0/model/hash/validator.py +307 -0
  86. clm/layer0/model/interpreter.py +275 -0
  87. clm/layer0/model/pagination.py +45 -0
  88. clm/layer0/model/pcard.py +716 -0
  89. clm/layer0/model/schema.py +44 -0
  90. clm/layer0/model/utils/__init__.py +1 -0
  91. clm/layer0/model/utils/content_analyzer.py +152 -0
  92. clm/layer0/model/validators/__init__.py +1 -0
  93. clm/layer0/model/validators/base_validator.py +44 -0
  94. clm/layer0/model/validators/binary_validator.py +66 -0
  95. clm/layer0/model/validators/text_validator.py +82 -0
  96. clm/layer0/model/validators/validation_registry.py +49 -0
  97. clm/layer0/model/vcard.py +823 -0
  98. clm/layer0/model/vcard_ext/__init__.py +39 -0
  99. clm/layer0/model/vcard_ext/core.py +60 -0
  100. clm/layer0/model/vcard_ext/network.py +23 -0
  101. clm/layer0/model/vcard_ext/observability.py +63 -0
  102. clm/layer0/model/vcard_ext/storage.py +60 -0
  103. clm/layer0/model/vcard_ext/vendors.py +211 -0
  104. clm/layer0/model/vcard_sandwich.py +119 -0
  105. clm/layer0/model/vcard_vocabulary.py +463 -0
  106. clm/layer0/model/workflow.py +274 -0
  107. clm/layer0/narrative.py +431 -0
  108. clm/layer0/ontology/collection.py +125 -0
  109. clm/layer0/parser.py +429 -0
  110. clm/layer0/relations.py +73 -0
  111. clm/layer0/rule_evaluator.py +251 -0
  112. clm/layer0/schema/__init__.py +38 -0
  113. clm/layer0/schema/ddl.py +97 -0
  114. clm/layer0/templates/abstract.yaml +99 -0
  115. clm/layer0/templates/balanced.yaml +253 -0
  116. clm/layer0/templates/concrete.yaml +172 -0
  117. clm/layer0/type_registry.py +223 -0
  118. clm/layer0/types.py +204 -0
  119. clm/layer0/utils.py +886 -0
  120. clm/layer0/verifier_core.py +262 -0
  121. clm/layer1/__init__.py +31 -0
  122. clm/layer1/action_dispatcher.py +275 -0
  123. clm/layer1/adapter_registry.py +233 -0
  124. clm/layer1/algebra_baldwin_generators.py +192 -0
  125. clm/layer1/arrow_compiler.py +525 -0
  126. clm/layer1/batch_processor.py +152 -0
  127. clm/layer1/cel/__init__.py +27 -0
  128. clm/layer1/cel/builder.py +173 -0
  129. clm/layer1/engine.py +759 -0
  130. clm/layer1/engine_async.py +89 -0
  131. clm/layer1/eval_callback.py +49 -0
  132. clm/layer1/gate_composer.py +229 -0
  133. clm/layer1/hoare_sandwich.py +298 -0
  134. clm/layer1/identity_guard.py +348 -0
  135. clm/layer1/matrix.py +138 -0
  136. clm/layer1/neural_runtime.py +221 -0
  137. clm/layer1/operations/__init__.py +123 -0
  138. clm/layer1/operations/augment.py +156 -0
  139. clm/layer1/operations/base.py +109 -0
  140. clm/layer1/operations/builtins.py +338 -0
  141. clm/layer1/operations/dispatch.py +144 -0
  142. clm/layer1/operations/exclude.py +107 -0
  143. clm/layer1/operations/handle.py +197 -0
  144. clm/layer1/operations/identity.py +139 -0
  145. clm/layer1/operations/invert.py +66 -0
  146. clm/layer1/operations/loader.py +116 -0
  147. clm/layer1/operations/manipulators/__init__.py +23 -0
  148. clm/layer1/operations/manipulators/document.py +211 -0
  149. clm/layer1/operations/manipulators/media.py +134 -0
  150. clm/layer1/operations/manipulators/structured.py +159 -0
  151. clm/layer1/operations/manipulators/tabular.py +334 -0
  152. clm/layer1/operations/port.py +65 -0
  153. clm/layer1/operations/services.py +529 -0
  154. clm/layer1/operations/split.py +117 -0
  155. clm/layer1/operations/substitute.py +75 -0
  156. clm/layer1/petri.py +1052 -0
  157. clm/layer1/pocketflow_orchestrator.py +408 -0
  158. clm/layer1/pocketflow_scheduler.py +519 -0
  159. clm/layer1/protocols.py +197 -0
  160. clm/layer1/runtime_adapter.py +718 -0
  161. clm/layer1/runtimes/__init__.py +71 -0
  162. clm/layer1/runtimes/_generated_assertions.py +125 -0
  163. clm/layer1/runtimes/_generated_types.py +42 -0
  164. clm/layer1/runtimes/base.py +265 -0
  165. clm/layer1/runtimes/binary.py +101 -0
  166. clm/layer1/runtimes/factory.py +203 -0
  167. clm/layer1/runtimes/javascript.py +321 -0
  168. clm/layer1/runtimes/javascript_runtime.js +33 -0
  169. clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
  170. clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
  171. clm/layer1/runtimes/lambda_calc.py +126 -0
  172. clm/layer1/runtimes/python.py +288 -0
  173. clm/layer1/runtimes/python_runtime.py +41 -0
  174. clm/layer1/runtimes/script.py +114 -0
  175. clm/layer1/sandbox.py +334 -0
  176. clm/layer1/savepoint_guard.py +136 -0
  177. clm/layer1/sparse_net.py +122 -0
  178. clm/layer1/spec_tester.py +165 -0
  179. clm/layer1/store.py +295 -0
  180. clm/layer1/test_runner.py +498 -0
  181. clm/layer1/type_inspector.py +166 -0
  182. clm/layer1/verifier.py +772 -0
  183. clm/layer1/vm.py +91 -0
  184. clm/layer2/__init__.py +78 -0
  185. clm/layer2/card_collection.py +678 -0
  186. clm/layer2/classifier.py +479 -0
  187. clm/layer2/engine/__init__.py +1 -0
  188. clm/layer2/engine/abstract_sql_engine.py +320 -0
  189. clm/layer2/engine/base.py +193 -0
  190. clm/layer2/engine/duckdb_engine.py +576 -0
  191. clm/layer2/engine/sqlite_engine.py +518 -0
  192. clm/layer2/fiber/__init__.py +31 -0
  193. clm/layer2/fiber/context.py +89 -0
  194. clm/layer2/fiber/disposable.py +163 -0
  195. clm/layer2/fiber/fiber.py +125 -0
  196. clm/layer2/fiber/hoare.py +191 -0
  197. clm/layer2/file_io.py +376 -0
  198. clm/layer2/file_registrar.py +77 -0
  199. clm/layer2/ingest.py +202 -0
  200. clm/layer2/lineage.py +322 -0
  201. clm/layer2/mcard_fs.py +262 -0
  202. clm/layer2/merkle.py +11 -0
  203. clm/layer2/mime.py +244 -0
  204. clm/layer2/mime_detector.py +245 -0
  205. clm/layer2/private_collection.py +118 -0
  206. clm/layer2/shared_store.py +11 -0
  207. clm/layer2/storage/__init__.py +190 -0
  208. clm/layer2/storage/db_bitemporal.py +141 -0
  209. clm/layer2/storage/db_driver.py +331 -0
  210. clm/layer2/storage/db_hyperlinks.py +214 -0
  211. clm/layer2/storage/db_pool.py +139 -0
  212. clm/layer2/storage/db_repository.py +229 -0
  213. clm/layer2/storage/savepoint.py +78 -0
  214. clm/layer2/storage/sqlite_wal.py +74 -0
  215. clm/layer2/tridb.py +245 -0
  216. clm/layer2/vcard_receipt.py +71 -0
  217. clm/layer2/vfs.py +17 -0
  218. clm/layer3/__init__.py +141 -0
  219. clm/layer3/agency/__init__.py +19 -0
  220. clm/layer3/agency/config.py +220 -0
  221. clm/layer3/agency/providers/__init__.py +15 -0
  222. clm/layer3/agency/providers/base.py +91 -0
  223. clm/layer3/agency/providers/mlc_llm.py +169 -0
  224. clm/layer3/agency/providers/ollama.py +203 -0
  225. clm/layer3/agency/router.py +71 -0
  226. clm/layer3/agency/runtime.py +524 -0
  227. clm/layer3/gateway.py +353 -0
  228. clm/layer3/open_interpreter_adapter.py +159 -0
  229. clm/layer3/rag/__init__.py +70 -0
  230. clm/layer3/rag/cli.py +228 -0
  231. clm/layer3/rag/config.py +153 -0
  232. clm/layer3/rag/embeddings/__init__.py +16 -0
  233. clm/layer3/rag/embeddings/base.py +79 -0
  234. clm/layer3/rag/embeddings/ollama.py +211 -0
  235. clm/layer3/rag/embeddings/vision.py +290 -0
  236. clm/layer3/rag/engine.py +276 -0
  237. clm/layer3/rag/graph/__init__.py +22 -0
  238. clm/layer3/rag/graph/community.py +198 -0
  239. clm/layer3/rag/graph/engine.py +433 -0
  240. clm/layer3/rag/graph/extractor.py +255 -0
  241. clm/layer3/rag/graph/schema.py +115 -0
  242. clm/layer3/rag/graph/store.py +641 -0
  243. clm/layer3/rag/indexer.py +333 -0
  244. clm/layer3/rag/llm_providers.json +52 -0
  245. clm/layer3/rag/semantic_versioning.py +313 -0
  246. clm/layer3/rag/vector/__init__.py +28 -0
  247. clm/layer3/rag/vector/handle_vector_store.py +749 -0
  248. clm/layer3/rag/vector/schema.py +151 -0
  249. clm/layer3/rag/vector/store.py +632 -0
  250. clm/layer3/reticulum/__init__.py +55 -0
  251. clm/layer3/reticulum/bridge.py +74 -0
  252. clm/layer3/reticulum/daemon.py +88 -0
  253. clm/layer3/reticulum/discovery.py +164 -0
  254. clm/layer3/reticulum/identity.py +107 -0
  255. clm/layer3/reticulum/media.py +148 -0
  256. clm/layer3/reticulum/transport.py +133 -0
  257. clm/layer3/satori/__init__.py +209 -0
  258. clm/layer3/satori/alpha_conversion.py +168 -0
  259. clm/layer3/satori/beta_reduction.py +359 -0
  260. clm/layer3/satori/continuation.py +38 -0
  261. clm/layer3/satori/eta_conversion.py +205 -0
  262. clm/layer3/satori/free_variables.py +163 -0
  263. clm/layer3/satori/io_effects.py +370 -0
  264. clm/layer3/satori/lambda_runtime.py +726 -0
  265. clm/layer3/satori/lambda_term.py +230 -0
  266. clm/layer3/satori/sheaf_audit.py +261 -0
  267. clm/layer3/satori/speech_act.py +279 -0
  268. clm/layer4/README.md +9 -0
  269. clm/layer4/__init__.py +67 -0
  270. clm/layer4/_sovereign_executor.py +580 -0
  271. clm/layer4/action_commands.py +719 -0
  272. clm/layer4/api.py +236 -0
  273. clm/layer4/bootstrap/__init__.py +13 -0
  274. clm/layer4/bootstrap/bootstrapper.py +118 -0
  275. clm/layer4/bootstrap/cleanup.py +44 -0
  276. clm/layer4/bootstrap/clm_hooks.py +49 -0
  277. clm/layer4/bootstrap/genesis.py +77 -0
  278. clm/layer4/bootstrap/injection.py +125 -0
  279. clm/layer4/cli.py +549 -0
  280. clm/layer4/collection_manager.py +553 -0
  281. clm/layer4/commands/__init__.py +81 -0
  282. clm/layer4/commands/action_cmd.py +42 -0
  283. clm/layer4/commands/check_cmd.py +82 -0
  284. clm/layer4/commands/envelope.py +73 -0
  285. clm/layer4/commands/evaluate_cmd.py +224 -0
  286. clm/layer4/commands/mcard_cmd.py +24 -0
  287. clm/layer4/commands/petri_cmd.py +92 -0
  288. clm/layer4/commands/tridb_cmd.py +319 -0
  289. clm/layer4/commands/version_cmd.py +27 -0
  290. clm/layer4/diff_commands.py +167 -0
  291. clm/layer4/eoa_sponsor.py +232 -0
  292. clm/layer4/events.py +69 -0
  293. clm/layer4/flux_gateway.py +133 -0
  294. clm/layer4/gateway.py +91 -0
  295. clm/layer4/loader.py +307 -0
  296. clm/layer4/mcard_commands.py +166 -0
  297. clm/layer4/membrane.py +273 -0
  298. clm/layer4/navigation.py +50 -0
  299. clm/layer4/protocol/protocol_entry_python.py +108 -0
  300. clm/layer4/protocol/python_sync.py +126 -0
  301. clm/layer4/runner.py +150 -0
  302. clm/layer4/satori.py +262 -0
  303. clm/layer4/server/python_sync_client.py +150 -0
  304. clm/layer4/server/python_sync_server.py +139 -0
  305. clm/layer4/server/service_api.py +315 -0
  306. clm/layer4/server/start_servers.py +107 -0
  307. clm/layer4/server/stop_servers.py +24 -0
  308. clm/layer4/simulated_optimization_fix.py +0 -0
  309. clm/layer4/storage.py +463 -0
  310. clm/layer4/tridb_admin.py +268 -0
  311. clm/layer4/websocket/test_ws_client.py +104 -0
  312. clm/layer4/websocket/test_ws_server.py +138 -0
  313. clm/layer4/websocket/ws_server_local.py +160 -0
  314. clm/layer5/__init__.py +51 -0
  315. clm/layer5/async_batch_writer.py +133 -0
  316. clm/layer5/learning.py +276 -0
  317. clm/layer5/telemetry_hook.py +107 -0
  318. clm/layer5/type_lattice.py +238 -0
  319. clm/py.typed +0 -0
  320. clm/schemas/__init__.py +652 -0
  321. clm/schemas/abstract_face_schema.yaml +77 -0
  322. clm/schemas/cel_schema.yaml +416 -0
  323. clm/schemas/lifecycle/__init__.py +45 -0
  324. clm/schemas/lifecycle/models.py +123 -0
  325. clm/schemas/lifecycle/service.py +364 -0
  326. clm/schemas/mcard_schema.sql +192 -0
  327. clm/schemas/mcard_vector_schema.sql +283 -0
  328. clm/schemas/mlp-context-v1.json +91 -0
  329. clm_kernel-0.0.0.dist-info/METADATA +121 -0
  330. clm_kernel-0.0.0.dist-info/RECORD +333 -0
  331. clm_kernel-0.0.0.dist-info/WHEEL +4 -0
  332. clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
  333. clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,749 @@
1
+ """
2
+ Handle-Aware Vector Store for Semantic Versioning
3
+
4
+ Extends MCardVectorStore to enable semantic similarity detection
5
+ across MCard versions linked to handles.
6
+
7
+ Key Features:
8
+ - Link MCards to handles during indexing
9
+ - Compare versions within a handle by semantic similarity
10
+ - Filter searches by handle
11
+ - Sort version history by distance to current or query
12
+ - Automatic upgrade type classification based on semantic delta
13
+
14
+ See: docs/architecture/Handle_Vector_Similarity_Design.md
15
+ """
16
+
17
+ import logging
18
+ from dataclasses import dataclass
19
+ from datetime import datetime, timezone
20
+ from typing import Any
21
+
22
+ from clm.layer0.model.card import MCard
23
+
24
+ from .schema import HANDLE_VECTOR_SCHEMAS
25
+ from .store import (
26
+ MCardVectorStore,
27
+ cosine_similarity,
28
+ deserialize_vector,
29
+ euclidean_distance,
30
+ )
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ # ─────────────────────────────────────────────────────────────────────────────
36
+ # Data Classes
37
+ # ─────────────────────────────────────────────────────────────────────────────
38
+
39
+
40
+ @dataclass
41
+ class HandleVersion:
42
+ """A version in a handle's history with embedding info."""
43
+
44
+ hash: str
45
+ version_order: int # 0 = current, 1 = previous, etc.
46
+ is_current: bool
47
+ created_at: str
48
+ parent_hash: str | None = None
49
+ embedding_id: int | None = None
50
+ semantic_delta: float | None = None
51
+ upgrade_type: str | None = None
52
+
53
+ @property
54
+ def has_embedding(self) -> bool:
55
+ return self.embedding_id is not None
56
+
57
+
58
+ @dataclass
59
+ class VersionSimilarityResult:
60
+ """Result from version similarity comparison."""
61
+
62
+ hash: str
63
+ version_order: int
64
+ similarity_to_current: float # Cosine similarity to current version
65
+ distance_to_current: float # Euclidean distance to current version
66
+ similarity_to_query: float | None = None # If query provided
67
+ parent_hash: str | None = None
68
+ upgrade_type: str | None = None
69
+
70
+
71
+ # ─────────────────────────────────────────────────────────────────────────────
72
+ # Upgrade Type Classification
73
+ # ─────────────────────────────────────────────────────────────────────────────
74
+
75
+
76
+ def classify_upgrade_type(semantic_delta: float) -> str:
77
+ """
78
+ Classify upgrade type based on semantic similarity to parent.
79
+
80
+ Thresholds:
81
+ - >= 0.95: trivial (formatting, typo fixes)
82
+ - 0.85 - 0.94: minor (small edits, clarifications)
83
+ - 0.70 - 0.84: major (significant content changes)
84
+ - < 0.70: breaking (major semantic drift)
85
+
86
+ Args:
87
+ semantic_delta: Cosine similarity to parent version
88
+
89
+ Returns:
90
+ Upgrade type string: 'trivial', 'minor', 'major', or 'breaking'
91
+ """
92
+ if semantic_delta >= 0.95:
93
+ return "trivial"
94
+ elif semantic_delta >= 0.85:
95
+ return "minor"
96
+ elif semantic_delta >= 0.70:
97
+ return "major"
98
+ else:
99
+ return "breaking"
100
+
101
+
102
+ # ─────────────────────────────────────────────────────────────────────────────
103
+ # Handle-Aware Vector Store
104
+ # ─────────────────────────────────────────────────────────────────────────────
105
+
106
+
107
+ class HandleVectorStore(MCardVectorStore):
108
+ """
109
+ Extended vector store with handle-aware version similarity.
110
+
111
+ This class implements the Handle-Hash Duality pattern with semantic
112
+ awareness, enabling:
113
+
114
+ - Version tracking via handles
115
+ - Semantic similarity detection across versions
116
+ - Intelligent upgrade classification
117
+ - Cross-version semantic search
118
+
119
+ Architecture:
120
+ - Handles remain stable identifiers (Proxy Pattern)
121
+ - Hashes reference immutable content (Content-Addressing)
122
+ - Embeddings enable semantic understanding (Vector Search)
123
+
124
+ Usage:
125
+ store = HandleVectorStore(db_path="vectors.db")
126
+
127
+ # Index with handle association
128
+ store.index_with_handle(mcard, handle="my_document")
129
+
130
+ # Get versions sorted by similarity to current
131
+ versions = store.get_versions_by_similarity("my_document")
132
+
133
+ # Find versions similar to a query
134
+ versions = store.search_handle_versions("my_document", "machine learning")
135
+
136
+ # Compute all pairwise distances
137
+ distances = store.compute_version_distances("my_document")
138
+
139
+ See Also:
140
+ - MCardVectorStore: Base vector store implementation
141
+ - docs/architecture/Handle_Vector_Similarity_Design.md
142
+ - docs/architecture/Handle-Hash_Duality_and_Scale-Free_Architecture.md
143
+ """
144
+
145
+ def __init__(self, *args, **kwargs):
146
+ """
147
+ Initialize HandleVectorStore.
148
+
149
+ Inherits all arguments from MCardVectorStore and adds
150
+ handle-version tables for semantic versioning.
151
+ """
152
+ super().__init__(*args, **kwargs)
153
+ self._init_handle_tables()
154
+
155
+ def _init_handle_tables(self):
156
+ """Create handle-vector bridge tables for semantic versioning."""
157
+ cursor = self.conn.cursor()
158
+
159
+ # Create handle-version-vector bridge table
160
+ cursor.execute(HANDLE_VECTOR_SCHEMAS["handle_version_vectors"])
161
+
162
+ # Create indexes (split by semicolons and execute each)
163
+ for statement in HANDLE_VECTOR_SCHEMAS["handle_version_vectors_indexes"].split(
164
+ ";"
165
+ ):
166
+ statement = statement.strip()
167
+ if statement:
168
+ cursor.execute(statement)
169
+
170
+ # Create similarity cache table
171
+ cursor.execute(HANDLE_VECTOR_SCHEMAS["similarity_cache"])
172
+ cursor.execute(HANDLE_VECTOR_SCHEMAS["similarity_cache_index"])
173
+
174
+ self.conn.commit()
175
+ logger.debug("Initialized handle-vector bridge tables")
176
+
177
+ # ─────────────────────────────────────────────────────────────────────────
178
+ # Indexing with Handle Association
179
+ # ─────────────────────────────────────────────────────────────────────────
180
+
181
+ def index_with_handle(
182
+ self, mcard: MCard, handle: str, is_current: bool = True, chunk: bool = True
183
+ ) -> int:
184
+ """
185
+ Index an MCard and associate it with a handle.
186
+
187
+ This method:
188
+ 1. Indexes the MCard content with embeddings (via base class)
189
+ 2. Links the MCard hash to the handle with version tracking
190
+ 3. Computes semantic delta from parent version (if exists)
191
+ 4. Classifies the upgrade type based on similarity
192
+
193
+ Args:
194
+ mcard: MCard to index
195
+ handle: Handle name to associate with
196
+ is_current: Whether this is the current version
197
+ chunk: Whether to chunk long content
198
+
199
+ Returns:
200
+ Number of vectors indexed (0 if content couldn't be indexed)
201
+
202
+ Example:
203
+ >>> doc_v1 = create_mcard("Introduction to ML...")
204
+ >>> store.index_with_handle(doc_v1, handle="ml_intro")
205
+ 1
206
+ >>> doc_v2 = create_mcard("Introduction to ML with more detail...")
207
+ >>> store.index_with_handle(doc_v2, handle="ml_intro")
208
+ 1
209
+ """
210
+ # Standard indexing (generates embeddings)
211
+ indexed = self.index(mcard, chunk=chunk)
212
+
213
+ if indexed == 0:
214
+ logger.warning(
215
+ f"MCard {mcard.hash[:8]} could not be indexed, skipping handle link"
216
+ )
217
+ return 0
218
+
219
+ cursor = self.conn.cursor()
220
+ now = datetime.now(timezone.utc).isoformat()
221
+
222
+ # Get embedding ID for this hash (chunk 0)
223
+ cursor.execute(
224
+ """
225
+ SELECT id FROM mcard_vector_metadata
226
+ WHERE hash = ? AND chunk_index = 0 LIMIT 1
227
+ """,
228
+ (mcard.hash,),
229
+ )
230
+ row = cursor.fetchone()
231
+ embedding_id = row[0] if row else None
232
+
233
+ # Get current version info for calculating semantic delta
234
+ parent_hash = None
235
+ semantic_delta = None
236
+ upgrade_type = None
237
+
238
+ if is_current:
239
+ # Get the current version before updating
240
+ cursor.execute(
241
+ """
242
+ SELECT hash, embedding_id FROM handle_version_vectors
243
+ WHERE handle = ? AND is_current = TRUE LIMIT 1
244
+ """,
245
+ (handle,),
246
+ )
247
+ current_row = cursor.fetchone()
248
+
249
+ if current_row:
250
+ parent_hash = current_row[0]
251
+ parent_embedding_id = current_row[1]
252
+
253
+ # Calculate semantic delta if both have embeddings
254
+ if embedding_id and parent_embedding_id:
255
+ new_embedding = self._get_embedding(mcard.hash)
256
+ parent_embedding = self._get_embedding(parent_hash)
257
+
258
+ if new_embedding and parent_embedding:
259
+ semantic_delta = cosine_similarity(
260
+ new_embedding, parent_embedding
261
+ )
262
+ upgrade_type = classify_upgrade_type(semantic_delta)
263
+ logger.debug(
264
+ f"Semantic delta for {handle}: {semantic_delta:.4f} ({upgrade_type})"
265
+ )
266
+
267
+ # Shift existing versions down
268
+ cursor.execute(
269
+ """
270
+ UPDATE handle_version_vectors
271
+ SET version_order = version_order + 1, is_current = FALSE
272
+ WHERE handle = ?
273
+ """,
274
+ (handle,),
275
+ )
276
+ version_order = 0
277
+ else:
278
+ # Append as non-current version
279
+ cursor.execute(
280
+ """
281
+ SELECT COALESCE(MAX(version_order), -1) + 1
282
+ FROM handle_version_vectors
283
+ WHERE handle = ?
284
+ """,
285
+ (handle,),
286
+ )
287
+ version_order = cursor.fetchone()[0]
288
+
289
+ # Insert version record
290
+ cursor.execute(
291
+ """
292
+ INSERT OR REPLACE INTO handle_version_vectors
293
+ (handle, hash, parent_hash, version_order, is_current,
294
+ embedding_id, semantic_delta_from_parent, upgrade_type, created_at)
295
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
296
+ """,
297
+ (
298
+ handle,
299
+ mcard.hash,
300
+ parent_hash,
301
+ version_order,
302
+ is_current,
303
+ embedding_id,
304
+ semantic_delta,
305
+ upgrade_type,
306
+ now,
307
+ ),
308
+ )
309
+
310
+ self.conn.commit()
311
+ logger.info(
312
+ f"Indexed MCard {mcard.hash[:8]} for handle '{handle}' "
313
+ f"(v{version_order}, current={is_current}, type={upgrade_type})"
314
+ )
315
+ return indexed
316
+
317
+ # ─────────────────────────────────────────────────────────────────────────
318
+ # Version Retrieval
319
+ # ─────────────────────────────────────────────────────────────────────────
320
+
321
+ def get_handle_versions(self, handle: str) -> list[HandleVersion]:
322
+ """
323
+ Get all versions for a handle.
324
+
325
+ Args:
326
+ handle: Handle name to query
327
+
328
+ Returns:
329
+ List of HandleVersion objects, ordered by version_order (0 = current)
330
+
331
+ Example:
332
+ >>> versions = store.get_handle_versions("ml_intro")
333
+ >>> for v in versions:
334
+ ... print(f"v{v.version_order}: {v.hash[:8]} ({v.upgrade_type})")
335
+ v0: abc12345 (None) # current
336
+ v1: def67890 (minor)
337
+ v2: xyz11223 (major)
338
+ """
339
+ cursor = self.conn.cursor()
340
+ cursor.execute(
341
+ """
342
+ SELECT hash, version_order, is_current, embedding_id, created_at,
343
+ parent_hash, semantic_delta_from_parent, upgrade_type
344
+ FROM handle_version_vectors
345
+ WHERE handle = ?
346
+ ORDER BY version_order
347
+ """,
348
+ (handle,),
349
+ )
350
+
351
+ return [
352
+ HandleVersion(
353
+ hash=row[0],
354
+ version_order=row[1],
355
+ is_current=bool(row[2]),
356
+ embedding_id=row[3],
357
+ created_at=row[4],
358
+ parent_hash=row[5],
359
+ semantic_delta=row[6],
360
+ upgrade_type=row[7],
361
+ )
362
+ for row in cursor.fetchall()
363
+ ]
364
+
365
+ def get_handle_version_hashes(self, handle: str) -> list[str]:
366
+ """
367
+ Get all version hashes for a handle.
368
+
369
+ Args:
370
+ handle: Handle name to query
371
+
372
+ Returns:
373
+ List of hashes, ordered by version_order
374
+ """
375
+ versions = self.get_handle_versions(handle)
376
+ return [v.hash for v in versions]
377
+
378
+ def get_current_version(self, handle: str) -> HandleVersion | None:
379
+ """
380
+ Get the current version for a handle.
381
+
382
+ Args:
383
+ handle: Handle name to query
384
+
385
+ Returns:
386
+ HandleVersion for current version, or None if handle not found
387
+ """
388
+ cursor = self.conn.cursor()
389
+ cursor.execute(
390
+ """
391
+ SELECT hash, version_order, is_current, embedding_id, created_at,
392
+ parent_hash, semantic_delta_from_parent, upgrade_type
393
+ FROM handle_version_vectors
394
+ WHERE handle = ? AND is_current = TRUE
395
+ LIMIT 1
396
+ """,
397
+ (handle,),
398
+ )
399
+
400
+ row = cursor.fetchone()
401
+ if not row:
402
+ return None
403
+
404
+ return HandleVersion(
405
+ hash=row[0],
406
+ version_order=row[1],
407
+ is_current=bool(row[2]),
408
+ embedding_id=row[3],
409
+ created_at=row[4],
410
+ parent_hash=row[5],
411
+ semantic_delta=row[6],
412
+ upgrade_type=row[7],
413
+ )
414
+
415
+ # ─────────────────────────────────────────────────────────────────────────
416
+ # Semantic Similarity Operations
417
+ # ─────────────────────────────────────────────────────────────────────────
418
+
419
+ def get_versions_by_similarity(
420
+ self, handle: str, reference_hash: str = None, metric: str = "cosine"
421
+ ) -> list[VersionSimilarityResult]:
422
+ """
423
+ Get versions for a handle, sorted by similarity to a reference.
424
+
425
+ Args:
426
+ handle: Handle name
427
+ reference_hash: Hash to compare against (default: current version)
428
+ metric: 'cosine' (higher = more similar) or 'euclidean' (lower = more similar)
429
+
430
+ Returns:
431
+ List of VersionSimilarityResult, sorted by similarity
432
+
433
+ Example:
434
+ >>> versions = store.get_versions_by_similarity("ml_intro")
435
+ >>> for v in versions:
436
+ ... print(f"v{v.version_order}: sim={v.similarity_to_current:.4f}")
437
+ v0: sim=1.0000 (current - self-similarity)
438
+ v1: sim=0.8234 (most similar to current)
439
+ v2: sim=0.6521 (least similar)
440
+ """
441
+ versions = self.get_handle_versions(handle)
442
+
443
+ if not versions:
444
+ return []
445
+
446
+ # Determine reference hash
447
+ if reference_hash is None:
448
+ for v in versions:
449
+ if v.is_current:
450
+ reference_hash = v.hash
451
+ break
452
+ if reference_hash is None and versions:
453
+ reference_hash = versions[0].hash
454
+
455
+ # Get reference embedding
456
+ ref_embedding = self._get_embedding(reference_hash)
457
+ if ref_embedding is None:
458
+ logger.warning(
459
+ f"No embedding found for reference hash {reference_hash[:8]}"
460
+ )
461
+ return []
462
+
463
+ # Calculate similarities
464
+ results = []
465
+ for v in versions:
466
+ if v.hash == reference_hash:
467
+ # Self-similarity
468
+ similarity = 1.0
469
+ distance = 0.0
470
+ else:
471
+ v_embedding = self._get_embedding(v.hash)
472
+ if v_embedding is None:
473
+ logger.debug(f"Skipping version {v.hash[:8]} - no embedding")
474
+ continue
475
+
476
+ similarity = cosine_similarity(ref_embedding, v_embedding)
477
+ distance = euclidean_distance(ref_embedding, v_embedding)
478
+
479
+ results.append(
480
+ VersionSimilarityResult(
481
+ hash=v.hash,
482
+ version_order=v.version_order,
483
+ similarity_to_current=similarity,
484
+ distance_to_current=distance,
485
+ parent_hash=v.parent_hash,
486
+ upgrade_type=v.upgrade_type,
487
+ )
488
+ )
489
+
490
+ # Sort by similarity (descending) or distance (ascending)
491
+ if metric == "cosine":
492
+ results.sort(key=lambda x: x.similarity_to_current, reverse=True)
493
+ else:
494
+ results.sort(key=lambda x: x.distance_to_current)
495
+
496
+ return results
497
+
498
+ def search_handle_versions(
499
+ self, handle: str, query: str, k: int = None
500
+ ) -> list[VersionSimilarityResult]:
501
+ """
502
+ Search within a handle's versions by semantic query.
503
+
504
+ Useful for finding which version of a document best matches
505
+ a specific concept or topic.
506
+
507
+ Args:
508
+ handle: Handle name to filter by
509
+ query: Search query text
510
+ k: Number of results (default from config)
511
+
512
+ Returns:
513
+ List of VersionSimilarityResult with query similarity scores
514
+
515
+ Example:
516
+ >>> results = store.search_handle_versions("ml_intro", "neural networks")
517
+ >>> for r in results:
518
+ ... print(f"v{r.version_order}: query_sim={r.similarity_to_query:.4f}")
519
+ v0: query_sim=0.8923 (current version mentions neural networks)
520
+ v1: query_sim=0.4123 (mentions AI but not directly)
521
+ """
522
+ k = k or self.config.top_k
523
+
524
+ # Get version hashes for this handle
525
+ version_hashes = self.get_handle_version_hashes(handle)
526
+ if not version_hashes:
527
+ return []
528
+
529
+ # Search with hash filter (uses base class method)
530
+ results = self.search(query, k=k, filter_hashes=version_hashes)
531
+
532
+ # Get version info for enrichment
533
+ versions = {v.hash: v for v in self.get_handle_versions(handle)}
534
+
535
+ # Enrich results with version info
536
+ enriched = []
537
+ for r in results:
538
+ v = versions.get(r.hash)
539
+ if v:
540
+ enriched.append(
541
+ VersionSimilarityResult(
542
+ hash=r.hash,
543
+ version_order=v.version_order,
544
+ similarity_to_current=0.0, # Not computed in this method
545
+ distance_to_current=0.0, # Not computed in this method
546
+ similarity_to_query=r.score,
547
+ parent_hash=v.parent_hash,
548
+ upgrade_type=v.upgrade_type,
549
+ )
550
+ )
551
+
552
+ return enriched
553
+
554
+ def compute_version_distances(
555
+ self, handle: str, cache: bool = True
556
+ ) -> dict[tuple[str, str], float]:
557
+ """
558
+ Compute pairwise cosine similarities between all versions of a handle.
559
+
560
+ Optionally caches results in the version_similarity_cache table.
561
+
562
+ Args:
563
+ handle: Handle name
564
+ cache: Whether to cache computed similarities
565
+
566
+ Returns:
567
+ Dict mapping (hash_a, hash_b) tuple to cosine similarity
568
+
569
+ Example:
570
+ >>> distances = store.compute_version_distances("ml_intro")
571
+ >>> for (h1, h2), sim in sorted(distances.items()):
572
+ ... print(f"{h1[:8]} <-> {h2[:8]}: {sim:.4f}")
573
+ """
574
+ versions = self.get_handle_versions(handle)
575
+ embeddings: dict[str, list[float]] = {}
576
+
577
+ for v in versions:
578
+ emb = self._get_embedding(v.hash)
579
+ if emb:
580
+ embeddings[v.hash] = emb
581
+
582
+ distances: dict[tuple[str, str], float] = {}
583
+ hashes = list(embeddings.keys())
584
+ now = datetime.now(timezone.utc).isoformat()
585
+
586
+ cursor = self.conn.cursor()
587
+
588
+ for i, h1 in enumerate(hashes):
589
+ for h2 in hashes[i:]:
590
+ if h1 == h2:
591
+ sim = 1.0
592
+ else:
593
+ sim = cosine_similarity(embeddings[h1], embeddings[h2])
594
+ dist = euclidean_distance(embeddings[h1], embeddings[h2])
595
+
596
+ # Cache the result
597
+ if cache:
598
+ try:
599
+ cursor.execute(
600
+ """
601
+ INSERT OR REPLACE INTO version_similarity_cache
602
+ (handle, hash_a, hash_b, similarity_score, distance_euclidean, computed_at)
603
+ VALUES (?, ?, ?, ?, ?, ?)
604
+ """,
605
+ (handle, h1, h2, sim, dist, now),
606
+ )
607
+ except Exception as e:
608
+ logger.warning(
609
+ f"Failed to cache similarity for {h1[:8]}<->{h2[:8]}: {e}"
610
+ )
611
+
612
+ distances[(h1, h2)] = sim
613
+ distances[(h2, h1)] = sim
614
+
615
+ if cache:
616
+ self.conn.commit()
617
+
618
+ return distances
619
+
620
+ # ─────────────────────────────────────────────────────────────────────────
621
+ # Embedding Retrieval (Internal)
622
+ # ─────────────────────────────────────────────────────────────────────────
623
+
624
+ def _get_embedding(self, hash: str, chunk_index: int = 0) -> list[float] | None:
625
+ """
626
+ Get embedding vector for a hash.
627
+
628
+ Args:
629
+ hash: MCard hash
630
+ chunk_index: Chunk index (default 0 for first/whole chunk)
631
+
632
+ Returns:
633
+ Embedding vector as list of floats, or None if not found
634
+ """
635
+ cursor = self.conn.cursor()
636
+
637
+ # Get metadata ID
638
+ cursor.execute(
639
+ """
640
+ SELECT id FROM mcard_vector_metadata
641
+ WHERE hash = ? AND chunk_index = ?
642
+ """,
643
+ (hash, chunk_index),
644
+ )
645
+ row = cursor.fetchone()
646
+
647
+ if not row:
648
+ return None
649
+
650
+ metadata_id = row[0]
651
+
652
+ # Get embedding from appropriate table
653
+ if self.has_vec_extension:
654
+ cursor.execute(
655
+ """
656
+ SELECT embedding FROM mcard_vec WHERE metadata_id = ?
657
+ """,
658
+ (metadata_id,),
659
+ )
660
+ else:
661
+ cursor.execute(
662
+ """
663
+ SELECT embedding FROM mcard_embeddings WHERE metadata_id = ?
664
+ """,
665
+ (metadata_id,),
666
+ )
667
+
668
+ row = cursor.fetchone()
669
+ if row:
670
+ return deserialize_vector(row[0])
671
+ return None
672
+
673
+ # ─────────────────────────────────────────────────────────────────────────
674
+ # Utility Methods
675
+ # ─────────────────────────────────────────────────────────────────────────
676
+
677
+ def list_handles(self) -> list[str]:
678
+ """
679
+ List all handles with indexed versions.
680
+
681
+ Returns:
682
+ List of unique handle names
683
+ """
684
+ cursor = self.conn.cursor()
685
+ cursor.execute(
686
+ "SELECT DISTINCT handle FROM handle_version_vectors ORDER BY handle"
687
+ )
688
+ return [row[0] for row in cursor.fetchall()]
689
+
690
+ def count_versions(self, handle: str) -> int:
691
+ """
692
+ Count versions for a handle.
693
+
694
+ Args:
695
+ handle: Handle name
696
+
697
+ Returns:
698
+ Number of versions
699
+ """
700
+ cursor = self.conn.cursor()
701
+ cursor.execute(
702
+ "SELECT COUNT(*) FROM handle_version_vectors WHERE handle = ?", (handle,)
703
+ )
704
+ return cursor.fetchone()[0]
705
+
706
+ def delete_handle(self, handle: str) -> int:
707
+ """
708
+ Delete all version records for a handle.
709
+
710
+ Note: This only removes the handle-version associations,
711
+ not the underlying MCard embeddings.
712
+
713
+ Args:
714
+ handle: Handle name
715
+
716
+ Returns:
717
+ Number of version records deleted
718
+ """
719
+ cursor = self.conn.cursor()
720
+
721
+ # Delete similarity cache
722
+ cursor.execute(
723
+ "DELETE FROM version_similarity_cache WHERE handle = ?", (handle,)
724
+ )
725
+
726
+ # Delete version records
727
+ cursor.execute("DELETE FROM handle_version_vectors WHERE handle = ?", (handle,))
728
+ deleted = cursor.rowcount
729
+
730
+ self.conn.commit()
731
+ logger.info(f"Deleted {deleted} version records for handle '{handle}'")
732
+ return deleted
733
+
734
+ def get_info(self) -> dict[str, Any]:
735
+ """Get extended vector store information including handle stats."""
736
+ info = super().get_info()
737
+
738
+ # Add handle-specific stats
739
+ cursor = self.conn.cursor()
740
+ cursor.execute("SELECT COUNT(DISTINCT handle) FROM handle_version_vectors")
741
+ info["handle_count"] = cursor.fetchone()[0]
742
+
743
+ cursor.execute("SELECT COUNT(*) FROM handle_version_vectors")
744
+ info["version_count"] = cursor.fetchone()[0]
745
+
746
+ cursor.execute("SELECT COUNT(*) FROM version_similarity_cache")
747
+ info["cached_similarities"] = cursor.fetchone()[0]
748
+
749
+ return info