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,632 @@
1
+ """
2
+ MCard Vector Store
3
+
4
+ SQLite-based vector storage for MCard semantic search.
5
+ Supports sqlite-vec extension for optimized KNN queries,
6
+ with fallback to brute-force similarity when extension unavailable.
7
+ """
8
+
9
+ import logging
10
+ import sqlite3
11
+ import struct
12
+ from datetime import datetime, timezone
13
+ from typing import Any
14
+
15
+ from clm.cross_cutting.config.config_constants import MAX_VECTOR_CONTENT_CHARS
16
+ from clm.layer0.model.card import MCard
17
+
18
+ from ..config import DEFAULT_RAG_CONFIG, RAGConfig
19
+ from ..embeddings import EmbeddingProvider, OllamaEmbeddingProvider
20
+ from .schema import VECTOR_SCHEMAS, get_vec0_schema
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ # ─────────────────────────────────────────────────────────────────────────────
26
+ # Vector Serialization Utilities
27
+ # ─────────────────────────────────────────────────────────────────────────────
28
+
29
+
30
+ def serialize_vector(vector: list[float]) -> bytes:
31
+ """
32
+ Serialize a float vector to bytes for SQLite storage.
33
+
34
+ Uses little-endian float32 format compatible with sqlite-vec.
35
+ """
36
+ return struct.pack(f"<{len(vector)}f", *vector)
37
+
38
+
39
+ def deserialize_vector(blob: bytes) -> list[float]:
40
+ """
41
+ Deserialize bytes back to a float vector.
42
+ """
43
+ count = len(blob) // 4 # 4 bytes per float32
44
+ return list(struct.unpack(f"<{count}f", blob))
45
+
46
+
47
+ def cosine_similarity(a: list[float], b: list[float]) -> float:
48
+ """
49
+ Calculate cosine similarity between two vectors.
50
+
51
+ Returns a value between -1 and 1, where 1 means identical direction.
52
+ """
53
+ if len(a) != len(b):
54
+ raise ValueError(f"Vector length mismatch: {len(a)} vs {len(b)}")
55
+
56
+ dot_product = sum(x * y for x, y in zip(a, b, strict=False))
57
+ norm_a = sum(x * x for x in a) ** 0.5
58
+ norm_b = sum(x * x for x in b) ** 0.5
59
+
60
+ if norm_a == 0 or norm_b == 0:
61
+ return 0.0
62
+
63
+ return dot_product / (norm_a * norm_b)
64
+
65
+
66
+ def euclidean_distance(a: list[float], b: list[float]) -> float:
67
+ """Calculate Euclidean (L2) distance between two vectors."""
68
+ if len(a) != len(b):
69
+ raise ValueError(f"Vector length mismatch: {len(a)} vs {len(b)}")
70
+
71
+ return sum((x - y) ** 2 for x, y in zip(a, b, strict=False)) ** 0.5
72
+
73
+
74
+ # ─────────────────────────────────────────────────────────────────────────────
75
+ # Search Result
76
+ # ─────────────────────────────────────────────────────────────────────────────
77
+
78
+
79
+ class VectorSearchResult:
80
+ """Result from a vector similarity search."""
81
+
82
+ def __init__(
83
+ self,
84
+ hash: str,
85
+ score: float,
86
+ chunk_index: int = 0,
87
+ chunk_text: str | None = None,
88
+ metadata: dict | None = None,
89
+ ):
90
+ self.hash = hash
91
+ self.score = score # Similarity score (higher = more similar)
92
+ self.chunk_index = chunk_index
93
+ self.chunk_text = chunk_text
94
+ self.metadata = metadata or {}
95
+
96
+ def __repr__(self):
97
+ return f"VectorSearchResult(hash={self.hash[:8]}..., score={self.score:.4f})"
98
+
99
+
100
+ # ─────────────────────────────────────────────────────────────────────────────
101
+ # MCard Vector Store
102
+ # ─────────────────────────────────────────────────────────────────────────────
103
+
104
+
105
+ class MCardVectorStore:
106
+ """
107
+ Vector storage for MCard embeddings.
108
+
109
+ Features:
110
+ - Automatic embedding generation via Ollama
111
+ - sqlite-vec extension for optimized KNN (with fallback)
112
+ - Chunking for long documents
113
+ - Full-text search for hybrid retrieval
114
+
115
+ Usage:
116
+ store = MCardVectorStore(db_path="vectors.db")
117
+
118
+ # Index an MCard
119
+ store.index(mcard)
120
+
121
+ # Search similar content
122
+ results = store.search("What is MCard?", k=5)
123
+ for result in results:
124
+ print(f"{result.hash}: {result.score}")
125
+ """
126
+
127
+ def __init__(
128
+ self,
129
+ db_path: str = None,
130
+ config: RAGConfig = None,
131
+ embedding_provider: EmbeddingProvider = None,
132
+ ):
133
+ """
134
+ Initialize vector store.
135
+
136
+ Args:
137
+ db_path: Path to SQLite database (None = in-memory)
138
+ config: RAG configuration
139
+ embedding_provider: Custom embedding provider (default: Ollama)
140
+ """
141
+ self.config = config or DEFAULT_RAG_CONFIG
142
+ self.db_path = db_path or ":memory:"
143
+
144
+ # Initialize embedding provider
145
+ if embedding_provider:
146
+ self.embedder = embedding_provider
147
+ else:
148
+ self.embedder = OllamaEmbeddingProvider(
149
+ model=self.config.embedding_model, base_url=self.config.ollama_base_url
150
+ )
151
+
152
+ # Initialize database
153
+ self.conn = self._init_database()
154
+ self.has_vec_extension = self._try_load_vec_extension()
155
+
156
+ def _init_database(self) -> sqlite3.Connection:
157
+ """Initialize SQLite connection and create tables."""
158
+ conn = sqlite3.connect(self.db_path, check_same_thread=False)
159
+ cursor = conn.cursor()
160
+
161
+ # Create metadata and fallback tables
162
+ cursor.execute(VECTOR_SCHEMAS["metadata"])
163
+ cursor.execute(VECTOR_SCHEMAS["metadata_index"])
164
+ cursor.execute(VECTOR_SCHEMAS["embeddings"])
165
+
166
+ # Create FTS table for hybrid search
167
+ if self.config.enable_hybrid_search:
168
+ cursor.execute(VECTOR_SCHEMAS["fts"])
169
+
170
+ conn.commit()
171
+ logger.debug(f"Initialized vector store at {self.db_path}")
172
+ return conn
173
+
174
+ def _try_load_vec_extension(self) -> bool:
175
+ """Try to load sqlite-vec extension."""
176
+ try:
177
+ import sqlite_vec
178
+
179
+ self.conn.enable_load_extension(True)
180
+ sqlite_vec.load(self.conn)
181
+
182
+ # Create vec0 virtual table
183
+ schema = get_vec0_schema(self.embedder.dimensions)
184
+ self.conn.execute(schema)
185
+ self.conn.commit()
186
+
187
+ logger.info(
188
+ f"sqlite-vec extension loaded (dimensions: {self.embedder.dimensions})"
189
+ )
190
+ return True
191
+
192
+ except ImportError:
193
+ logger.warning(
194
+ "sqlite-vec not installed. Using fallback similarity search. "
195
+ "Install with: pip install sqlite-vec"
196
+ )
197
+ return False
198
+ except Exception as e:
199
+ logger.warning(f"Failed to load sqlite-vec: {e}. Using fallback.")
200
+ return False
201
+
202
+ def _chunk_text(self, text: str) -> list[str]:
203
+ """
204
+ Split text into overlapping chunks.
205
+
206
+ Args:
207
+ text: Input text
208
+
209
+ Returns:
210
+ List of text chunks
211
+ """
212
+ if len(text) <= self.config.chunk_size:
213
+ return [text]
214
+
215
+ chunks = []
216
+ start = 0
217
+
218
+ while start < len(text):
219
+ end = start + self.config.chunk_size
220
+
221
+ # Try to break at sentence boundary
222
+ if end < len(text):
223
+ # Look for sentence end within last 20% of chunk
224
+ search_start = int(end - self.config.chunk_size * 0.2)
225
+ for sep in [". ", ".\n", "!\n", "?\n", "\n\n"]:
226
+ pos = text.rfind(sep, search_start, end)
227
+ if pos > start:
228
+ end = pos + len(sep)
229
+ break
230
+
231
+ chunks.append(text[start:end].strip())
232
+ start = end - self.config.chunk_overlap
233
+
234
+ return [c for c in chunks if c] # Filter empty
235
+
236
+ def index(self, mcard: MCard, chunk: bool = True) -> int:
237
+ """
238
+ Index an MCard with its embedding(s).
239
+
240
+ Args:
241
+ mcard: MCard to index
242
+ chunk: Whether to chunk long content
243
+
244
+ Returns:
245
+ Number of vectors indexed
246
+ """
247
+ # Get content as text
248
+ content = mcard.get_content()
249
+ if isinstance(content, bytes):
250
+ try:
251
+ content = content.decode("utf-8")
252
+ except UnicodeDecodeError:
253
+ logger.warning(
254
+ f"Cannot decode MCard {mcard.hash[:8]} as text, skipping"
255
+ )
256
+ return 0
257
+
258
+ content = str(content).strip()
259
+ if not content:
260
+ logger.warning(f"MCard {mcard.hash[:8]} has empty content, skipping")
261
+ return 0
262
+
263
+ # Chunk if needed
264
+ if chunk and len(content) > self.config.chunk_size:
265
+ chunks = self._chunk_text(content)
266
+ else:
267
+ chunks = [content]
268
+
269
+ # Generate embeddings
270
+ try:
271
+ embeddings = self.embedder.embed_batch(chunks)
272
+ except Exception as e:
273
+ logger.error(
274
+ f"Failed to generate embeddings for {mcard.hash[:8]}: {e}",
275
+ exc_info=True,
276
+ )
277
+ return 0
278
+
279
+ now = datetime.now(timezone.utc).isoformat()
280
+ indexed = 0
281
+ cursor = self.conn.cursor()
282
+
283
+ for i, (chunk_text, embedding) in enumerate(
284
+ zip(chunks, embeddings, strict=False)
285
+ ):
286
+ try:
287
+ # Insert metadata
288
+ cursor.execute(
289
+ """
290
+ INSERT OR REPLACE INTO mcard_vector_metadata
291
+ (hash, model_name, dimensions, chunk_index, chunk_total, chunk_text, created_at)
292
+ VALUES (?, ?, ?, ?, ?, ?, ?)
293
+ """,
294
+ (
295
+ mcard.hash,
296
+ self.embedder.model_name,
297
+ len(embedding),
298
+ i,
299
+ len(chunks),
300
+ chunk_text[:500] if len(chunk_text) > 500 else chunk_text,
301
+ now,
302
+ ),
303
+ )
304
+
305
+ metadata_id = cursor.lastrowid
306
+ embedding_blob = serialize_vector(embedding)
307
+
308
+ # Insert into vec0 or fallback table
309
+ if self.has_vec_extension:
310
+ cursor.execute(
311
+ """
312
+ INSERT INTO mcard_vec (metadata_id, embedding)
313
+ VALUES (?, ?)
314
+ """,
315
+ (metadata_id, embedding_blob),
316
+ )
317
+ else:
318
+ cursor.execute(
319
+ """
320
+ INSERT OR REPLACE INTO mcard_embeddings (metadata_id, embedding)
321
+ VALUES (?, ?)
322
+ """,
323
+ (metadata_id, embedding_blob),
324
+ )
325
+
326
+ indexed += 1
327
+
328
+ except Exception as e:
329
+ logger.error(
330
+ f"Failed to index chunk {i} of {mcard.hash[:8]}: {e}", exc_info=True
331
+ )
332
+
333
+ # Index in FTS for hybrid search
334
+ if self.config.enable_hybrid_search:
335
+ try:
336
+ cursor.execute(
337
+ """
338
+ INSERT OR REPLACE INTO mcard_fts (hash, content)
339
+ VALUES (?, ?)
340
+ """,
341
+ (mcard.hash, content[:MAX_VECTOR_CONTENT_CHARS]),
342
+ ) # Limit FTS content
343
+ except Exception as e:
344
+ logger.warning(f"FTS indexing failed for {mcard.hash[:8]}: {e}")
345
+
346
+ self.conn.commit()
347
+ logger.debug(f"Indexed {indexed} vectors for MCard {mcard.hash[:8]}")
348
+ return indexed
349
+
350
+ def search(
351
+ self, query: str, k: int = None, filter_hashes: list[str] = None
352
+ ) -> list[VectorSearchResult]:
353
+ """
354
+ Search for similar MCards by semantic similarity.
355
+
356
+ Args:
357
+ query: Search query text
358
+ k: Number of results (default from config)
359
+ filter_hashes: Optional list of hashes to include
360
+
361
+ Returns:
362
+ List of VectorSearchResult sorted by similarity (descending)
363
+ """
364
+ k = k or self.config.top_k
365
+
366
+ # Generate query embedding
367
+ try:
368
+ query_embedding = self.embedder.embed(query)
369
+ except Exception as e:
370
+ logger.error(f"Failed to embed query: {e}", exc_info=True)
371
+ return []
372
+
373
+ if self.has_vec_extension:
374
+ return self._search_vec0(query_embedding, k, filter_hashes)
375
+ else:
376
+ return self._search_fallback(query_embedding, k, filter_hashes)
377
+
378
+ def _search_vec0(
379
+ self, query_embedding: list[float], k: int, filter_hashes: list[str] = None
380
+ ) -> list[VectorSearchResult]:
381
+ """Search using sqlite-vec KNN."""
382
+ query_blob = serialize_vector(query_embedding)
383
+
384
+ cursor = self.conn.cursor()
385
+
386
+ # KNN query using vec0 'k' constraint in a CTE for better optimization
387
+ sql = """
388
+ WITH matches AS (
389
+ SELECT metadata_id, distance
390
+ FROM mcard_vec
391
+ WHERE embedding MATCH ?
392
+ AND k = ?
393
+ )
394
+ SELECT
395
+ m.hash,
396
+ m.chunk_index,
397
+ m.chunk_text,
398
+ matches.distance
399
+ FROM matches
400
+ JOIN mcard_vector_metadata m ON matches.metadata_id = m.id
401
+ ORDER BY matches.distance
402
+ """
403
+
404
+ cursor.execute(sql, (query_blob, k * 2)) # Request more for post-filtering
405
+ rows = cursor.fetchall()
406
+
407
+ # Convert distance to similarity and deduplicate by hash
408
+ results = []
409
+ seen_hashes = set()
410
+
411
+ for hash_val, chunk_idx, chunk_text, distance in rows:
412
+ if filter_hashes and hash_val not in filter_hashes:
413
+ continue
414
+ if hash_val in seen_hashes and len(results) >= k:
415
+ continue
416
+
417
+ # Convert L2 distance to similarity score
418
+ similarity = 1.0 / (1.0 + distance)
419
+
420
+ if hash_val not in seen_hashes:
421
+ seen_hashes.add(hash_val)
422
+ results.append(
423
+ VectorSearchResult(
424
+ hash=hash_val,
425
+ score=similarity,
426
+ chunk_index=chunk_idx,
427
+ chunk_text=chunk_text,
428
+ )
429
+ )
430
+
431
+ if len(results) >= k:
432
+ break
433
+
434
+ return results
435
+
436
+ def _search_fallback(
437
+ self, query_embedding: list[float], k: int, filter_hashes: list[str] = None
438
+ ) -> list[VectorSearchResult]:
439
+ """Search using brute-force similarity calculation."""
440
+ cursor = self.conn.cursor()
441
+
442
+ # Get all embeddings
443
+ sql = """
444
+ SELECT m.hash, m.chunk_index, m.chunk_text, e.embedding
445
+ FROM mcard_embeddings e
446
+ JOIN mcard_vector_metadata m ON e.metadata_id = m.id
447
+ """
448
+
449
+ if filter_hashes:
450
+ placeholders = ",".join("?" * len(filter_hashes))
451
+ sql += f" WHERE m.hash IN ({placeholders})"
452
+ cursor.execute(sql, filter_hashes)
453
+ else:
454
+ cursor.execute(sql)
455
+
456
+ # Calculate similarities
457
+ candidates = []
458
+ for hash_val, chunk_idx, chunk_text, embedding_blob in cursor.fetchall():
459
+ embedding = deserialize_vector(embedding_blob)
460
+ similarity = cosine_similarity(query_embedding, embedding)
461
+ candidates.append((hash_val, chunk_idx, chunk_text, similarity))
462
+
463
+ # Sort by similarity and deduplicate
464
+ candidates.sort(key=lambda x: x[3], reverse=True)
465
+
466
+ results = []
467
+ seen_hashes = set()
468
+
469
+ for hash_val, chunk_idx, chunk_text, similarity in candidates:
470
+ if hash_val not in seen_hashes:
471
+ seen_hashes.add(hash_val)
472
+ results.append(
473
+ VectorSearchResult(
474
+ hash=hash_val,
475
+ score=similarity,
476
+ chunk_index=chunk_idx,
477
+ chunk_text=chunk_text,
478
+ )
479
+ )
480
+
481
+ if len(results) >= k:
482
+ break
483
+
484
+ return results
485
+
486
+ def hybrid_search(
487
+ self, query: str, k: int = None, vector_weight: float = 0.7
488
+ ) -> list[VectorSearchResult]:
489
+ """
490
+ Hybrid search combining vector similarity and full-text search.
491
+
492
+ Args:
493
+ query: Search query
494
+ k: Number of results
495
+ vector_weight: Weight for vector similarity (0-1)
496
+
497
+ Returns:
498
+ Combined and re-ranked results
499
+ """
500
+ k = k or self.config.top_k
501
+ fts_weight = 1.0 - vector_weight
502
+
503
+ # Vector search
504
+ vector_results = self.search(query, k * 2)
505
+ vector_scores = {r.hash: r.score * vector_weight for r in vector_results}
506
+
507
+ # FTS search
508
+ fts_scores = {}
509
+ if self.config.enable_hybrid_search:
510
+ cursor = self.conn.cursor()
511
+ try:
512
+ # Prepare FTS query - escape special characters and use OR
513
+ fts_query = " OR ".join(
514
+ f'"{word}"' for word in query.split() if word and len(word) > 1
515
+ )
516
+ if not fts_query:
517
+ fts_query = f'"{query}"'
518
+
519
+ cursor.execute(
520
+ """
521
+ SELECT hash, bm25(mcard_fts) as score
522
+ FROM mcard_fts
523
+ WHERE mcard_fts MATCH ?
524
+ ORDER BY score
525
+ LIMIT ?
526
+ """,
527
+ (fts_query, k * 2),
528
+ )
529
+
530
+ for hash_val, score in cursor.fetchall():
531
+ # Normalize BM25 score (lower is better)
532
+ fts_scores[hash_val] = (1.0 / (1.0 - score)) * fts_weight
533
+
534
+ except Exception as e:
535
+ logger.warning(f"FTS search failed: {e}")
536
+
537
+ # Combine scores
538
+ all_hashes = set(vector_scores.keys()) | set(fts_scores.keys())
539
+ combined = []
540
+
541
+ for hash_val in all_hashes:
542
+ v_score = vector_scores.get(hash_val, 0)
543
+ f_score = fts_scores.get(hash_val, 0)
544
+ total = v_score + f_score
545
+
546
+ # Get chunk info from vector results
547
+ chunk_text = None
548
+ for r in vector_results:
549
+ if r.hash == hash_val:
550
+ chunk_text = r.chunk_text
551
+ break
552
+
553
+ combined.append(
554
+ VectorSearchResult(
555
+ hash=hash_val,
556
+ score=total,
557
+ chunk_text=chunk_text,
558
+ metadata={"vector_score": v_score, "fts_score": f_score},
559
+ )
560
+ )
561
+
562
+ # Sort and return top k
563
+ combined.sort(key=lambda x: x.score, reverse=True)
564
+ return combined[:k]
565
+
566
+ def is_indexed(self, hash: str) -> bool:
567
+ """Check if an MCard is already indexed."""
568
+ cursor = self.conn.cursor()
569
+ cursor.execute(
570
+ "SELECT 1 FROM mcard_vector_metadata WHERE hash = ? LIMIT 1", (hash,)
571
+ )
572
+ return cursor.fetchone() is not None
573
+
574
+ def delete(self, hash: str) -> int:
575
+ """Delete all vectors for an MCard."""
576
+ cursor = self.conn.cursor()
577
+
578
+ # Get metadata IDs
579
+ cursor.execute("SELECT id FROM mcard_vector_metadata WHERE hash = ?", (hash,))
580
+ ids = [row[0] for row in cursor.fetchall()]
581
+
582
+ if not ids:
583
+ return 0
584
+
585
+ # Delete from all tables
586
+ placeholders = ",".join("?" * len(ids))
587
+
588
+ if self.has_vec_extension:
589
+ cursor.execute(
590
+ f"DELETE FROM mcard_vec WHERE metadata_id IN ({placeholders})", # nosec B608
591
+ ids,
592
+ )
593
+ else:
594
+ cursor.execute(
595
+ f"DELETE FROM mcard_embeddings WHERE metadata_id IN ({placeholders})", # nosec B608
596
+ ids,
597
+ )
598
+
599
+ cursor.execute(
600
+ f"DELETE FROM mcard_vector_metadata WHERE id IN ({placeholders})", # nosec B608
601
+ ids,
602
+ )
603
+
604
+ if self.config.enable_hybrid_search:
605
+ cursor.execute("DELETE FROM mcard_fts WHERE hash = ?", (hash,))
606
+
607
+ self.conn.commit()
608
+ return len(ids)
609
+
610
+ def count(self) -> int:
611
+ """Get total number of indexed vectors."""
612
+ cursor = self.conn.cursor()
613
+ cursor.execute("SELECT COUNT(*) FROM mcard_vector_metadata")
614
+ return cursor.fetchone()[0]
615
+
616
+ def count_unique(self) -> int:
617
+ """Get number of unique MCards indexed."""
618
+ cursor = self.conn.cursor()
619
+ cursor.execute("SELECT COUNT(DISTINCT hash) FROM mcard_vector_metadata")
620
+ return cursor.fetchone()[0]
621
+
622
+ def get_info(self) -> dict[str, Any]:
623
+ """Get vector store information."""
624
+ return {
625
+ "db_path": self.db_path,
626
+ "embedding_model": self.embedder.model_name,
627
+ "dimensions": self.embedder.dimensions,
628
+ "has_vec_extension": self.has_vec_extension,
629
+ "vector_count": self.count(),
630
+ "unique_mcards": self.count_unique(),
631
+ "hybrid_search_enabled": self.config.enable_hybrid_search,
632
+ }
@@ -0,0 +1,55 @@
1
+ """Layer 3 Reticulum Sovereign Mesh Subpackage (DEL-390-01, INV-390-01).
2
+
3
+ Encapsulates Reticulum mesh networking primitives:
4
+ - Node discovery and capability announcement (discovery.py)
5
+ - Authenticated P2P link session transport (transport.py)
6
+ - Sovereign DID identity mapping (identity.py)
7
+ - Multi-media MCard chunking and CAS streaming (media.py)
8
+ - Local loopback daemon and partition simulation (daemon.py)
9
+ - Satori conversational messaging bridge (bridge.py)
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from clm.layer3.reticulum.bridge import SatoriReticulumBridge
14
+ from clm.layer3.reticulum.daemon import ReticulumLocalDaemon
15
+ from clm.layer3.reticulum.discovery import (
16
+ RETICULUM_AUTOINTERFACE_PORT,
17
+ ReticulumDiscoveryEngine,
18
+ ReticulumDiscoveryService,
19
+ )
20
+ from clm.layer3.reticulum.identity import (
21
+ create_rns_identity_from_seed,
22
+ derive_destination_hash,
23
+ derive_destination_hex,
24
+ extract_raw_pubkey_from_did,
25
+ )
26
+ from clm.layer3.reticulum.media import (
27
+ fetch_mcard_resource,
28
+ inject_mcard_to_peer,
29
+ reassemble_chunks,
30
+ slice_into_chunks,
31
+ )
32
+ from clm.layer3.reticulum.transport import (
33
+ ReticulumLinkSession,
34
+ ReticulumTransport,
35
+ establish_link_pair,
36
+ )
37
+
38
+ __all__ = [
39
+ "RETICULUM_AUTOINTERFACE_PORT",
40
+ "ReticulumDiscoveryService",
41
+ "ReticulumDiscoveryEngine",
42
+ "ReticulumLinkSession",
43
+ "ReticulumTransport",
44
+ "establish_link_pair",
45
+ "create_rns_identity_from_seed",
46
+ "derive_destination_hash",
47
+ "derive_destination_hex",
48
+ "extract_raw_pubkey_from_did",
49
+ "fetch_mcard_resource",
50
+ "inject_mcard_to_peer",
51
+ "reassemble_chunks",
52
+ "slice_into_chunks",
53
+ "ReticulumLocalDaemon",
54
+ "SatoriReticulumBridge",
55
+ ]