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.
- clm/__init__.py +230 -0
- clm/__main__.py +25 -0
- clm/cross_cutting/__init__.py +50 -0
- clm/cross_cutting/_test_runner.py +36 -0
- clm/cross_cutting/async_utils.py +25 -0
- clm/cross_cutting/cel_builder.py +123 -0
- clm/cross_cutting/cel_converter.py +697 -0
- clm/cross_cutting/cel_engine.py +252 -0
- clm/cross_cutting/cel_ops.py +72 -0
- clm/cross_cutting/cel_ops_db.py +157 -0
- clm/cross_cutting/cel_ops_files.py +99 -0
- clm/cross_cutting/cel_ops_state.py +71 -0
- clm/cross_cutting/cel_ops_strings.py +64 -0
- clm/cross_cutting/cel_resolver.py +63 -0
- clm/cross_cutting/cel_slices.py +56 -0
- clm/cross_cutting/cel_store.py +330 -0
- clm/cross_cutting/clm_logger.py +140 -0
- clm/cross_cutting/config/__init__.py +155 -0
- clm/cross_cutting/config/config_constants.py +392 -0
- clm/cross_cutting/config/env_parameters.py +90 -0
- clm/cross_cutting/config/logging.py +378 -0
- clm/cross_cutting/config/settings.py +504 -0
- clm/cross_cutting/context.py +5 -0
- clm/cross_cutting/domain_models.py +55 -0
- clm/cross_cutting/domain_types.py +61 -0
- clm/cross_cutting/effects.py +67 -0
- clm/cross_cutting/errors.py +153 -0
- clm/cross_cutting/governance.py +331 -0
- clm/cross_cutting/guards.py +41 -0
- clm/cross_cutting/mcard.py +321 -0
- clm/cross_cutting/native.py +185 -0
- clm/cross_cutting/observability.py +289 -0
- clm/cross_cutting/pcard.py +434 -0
- clm/cross_cutting/registry.py +71 -0
- clm/cross_cutting/resources.py +84 -0
- clm/cross_cutting/secrets.py +44 -0
- clm/cross_cutting/subprocess_utils.py +118 -0
- clm/cross_cutting/telemetry.py +164 -0
- clm/cross_cutting/testing/__init__.py +8 -0
- clm/cross_cutting/testing/comparator.py +187 -0
- clm/cross_cutting/testing/datasets.py +87 -0
- clm/cross_cutting/timeutil.py +20 -0
- clm/cross_cutting/types/new_type_check.py +0 -0
- clm/cross_cutting/utils/__init__.py +1 -0
- clm/cross_cutting/utils/url_safety.py +50 -0
- clm/cross_cutting/vcard.py +560 -0
- clm/cross_cutting/writer.py +118 -0
- clm/layer0/README.md +50 -0
- clm/layer0/__init__.py +223 -0
- clm/layer0/algebra.py +60 -0
- clm/layer0/algebra_baldwin.py +133 -0
- clm/layer0/algebra_baldwin_generators.py +36 -0
- clm/layer0/algebra_equivalence.py +262 -0
- clm/layer0/algebra_ingest.py +306 -0
- clm/layer0/algebra_manifest_io.py +156 -0
- clm/layer0/assembler.py +481 -0
- clm/layer0/cel/__init__.py +23 -0
- clm/layer0/cel_eval.py +491 -0
- clm/layer0/codec.py +106 -0
- clm/layer0/context.py +122 -0
- clm/layer0/db.py +160 -0
- clm/layer0/exceptions.py +73 -0
- clm/layer0/fibration.py +106 -0
- clm/layer0/gates.py +50 -0
- clm/layer0/hash.py +80 -0
- clm/layer0/loader.py +826 -0
- clm/layer0/math_utils.py +163 -0
- clm/layer0/mcard.py +96 -0
- clm/layer0/model/__init__.py +0 -0
- clm/layer0/model/action.py +223 -0
- clm/layer0/model/card.py +280 -0
- clm/layer0/model/card_triad.py +427 -0
- clm/layer0/model/compatibility.py +60 -0
- clm/layer0/model/dictionary.py +46 -0
- clm/layer0/model/dots.py +331 -0
- clm/layer0/model/event_producer.py +88 -0
- clm/layer0/model/g_time.py +89 -0
- clm/layer0/model/handle.py +163 -0
- clm/layer0/model/hash/__init__.py +6 -0
- clm/layer0/model/hash/algorithms/__init__.py +3 -0
- clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
- clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
- clm/layer0/model/hash/constants.py +53 -0
- clm/layer0/model/hash/enums.py +60 -0
- clm/layer0/model/hash/validator.py +307 -0
- clm/layer0/model/interpreter.py +275 -0
- clm/layer0/model/pagination.py +45 -0
- clm/layer0/model/pcard.py +716 -0
- clm/layer0/model/schema.py +44 -0
- clm/layer0/model/utils/__init__.py +1 -0
- clm/layer0/model/utils/content_analyzer.py +152 -0
- clm/layer0/model/validators/__init__.py +1 -0
- clm/layer0/model/validators/base_validator.py +44 -0
- clm/layer0/model/validators/binary_validator.py +66 -0
- clm/layer0/model/validators/text_validator.py +82 -0
- clm/layer0/model/validators/validation_registry.py +49 -0
- clm/layer0/model/vcard.py +823 -0
- clm/layer0/model/vcard_ext/__init__.py +39 -0
- clm/layer0/model/vcard_ext/core.py +60 -0
- clm/layer0/model/vcard_ext/network.py +23 -0
- clm/layer0/model/vcard_ext/observability.py +63 -0
- clm/layer0/model/vcard_ext/storage.py +60 -0
- clm/layer0/model/vcard_ext/vendors.py +211 -0
- clm/layer0/model/vcard_sandwich.py +119 -0
- clm/layer0/model/vcard_vocabulary.py +463 -0
- clm/layer0/model/workflow.py +274 -0
- clm/layer0/narrative.py +431 -0
- clm/layer0/ontology/collection.py +125 -0
- clm/layer0/parser.py +429 -0
- clm/layer0/relations.py +73 -0
- clm/layer0/rule_evaluator.py +251 -0
- clm/layer0/schema/__init__.py +38 -0
- clm/layer0/schema/ddl.py +97 -0
- clm/layer0/templates/abstract.yaml +99 -0
- clm/layer0/templates/balanced.yaml +253 -0
- clm/layer0/templates/concrete.yaml +172 -0
- clm/layer0/type_registry.py +223 -0
- clm/layer0/types.py +204 -0
- clm/layer0/utils.py +886 -0
- clm/layer0/verifier_core.py +262 -0
- clm/layer1/__init__.py +31 -0
- clm/layer1/action_dispatcher.py +275 -0
- clm/layer1/adapter_registry.py +233 -0
- clm/layer1/algebra_baldwin_generators.py +192 -0
- clm/layer1/arrow_compiler.py +525 -0
- clm/layer1/batch_processor.py +152 -0
- clm/layer1/cel/__init__.py +27 -0
- clm/layer1/cel/builder.py +173 -0
- clm/layer1/engine.py +759 -0
- clm/layer1/engine_async.py +89 -0
- clm/layer1/eval_callback.py +49 -0
- clm/layer1/gate_composer.py +229 -0
- clm/layer1/hoare_sandwich.py +298 -0
- clm/layer1/identity_guard.py +348 -0
- clm/layer1/matrix.py +138 -0
- clm/layer1/neural_runtime.py +221 -0
- clm/layer1/operations/__init__.py +123 -0
- clm/layer1/operations/augment.py +156 -0
- clm/layer1/operations/base.py +109 -0
- clm/layer1/operations/builtins.py +338 -0
- clm/layer1/operations/dispatch.py +144 -0
- clm/layer1/operations/exclude.py +107 -0
- clm/layer1/operations/handle.py +197 -0
- clm/layer1/operations/identity.py +139 -0
- clm/layer1/operations/invert.py +66 -0
- clm/layer1/operations/loader.py +116 -0
- clm/layer1/operations/manipulators/__init__.py +23 -0
- clm/layer1/operations/manipulators/document.py +211 -0
- clm/layer1/operations/manipulators/media.py +134 -0
- clm/layer1/operations/manipulators/structured.py +159 -0
- clm/layer1/operations/manipulators/tabular.py +334 -0
- clm/layer1/operations/port.py +65 -0
- clm/layer1/operations/services.py +529 -0
- clm/layer1/operations/split.py +117 -0
- clm/layer1/operations/substitute.py +75 -0
- clm/layer1/petri.py +1052 -0
- clm/layer1/pocketflow_orchestrator.py +408 -0
- clm/layer1/pocketflow_scheduler.py +519 -0
- clm/layer1/protocols.py +197 -0
- clm/layer1/runtime_adapter.py +718 -0
- clm/layer1/runtimes/__init__.py +71 -0
- clm/layer1/runtimes/_generated_assertions.py +125 -0
- clm/layer1/runtimes/_generated_types.py +42 -0
- clm/layer1/runtimes/base.py +265 -0
- clm/layer1/runtimes/binary.py +101 -0
- clm/layer1/runtimes/factory.py +203 -0
- clm/layer1/runtimes/javascript.py +321 -0
- clm/layer1/runtimes/javascript_runtime.js +33 -0
- clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
- clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
- clm/layer1/runtimes/lambda_calc.py +126 -0
- clm/layer1/runtimes/python.py +288 -0
- clm/layer1/runtimes/python_runtime.py +41 -0
- clm/layer1/runtimes/script.py +114 -0
- clm/layer1/sandbox.py +334 -0
- clm/layer1/savepoint_guard.py +136 -0
- clm/layer1/sparse_net.py +122 -0
- clm/layer1/spec_tester.py +165 -0
- clm/layer1/store.py +295 -0
- clm/layer1/test_runner.py +498 -0
- clm/layer1/type_inspector.py +166 -0
- clm/layer1/verifier.py +772 -0
- clm/layer1/vm.py +91 -0
- clm/layer2/__init__.py +78 -0
- clm/layer2/card_collection.py +678 -0
- clm/layer2/classifier.py +479 -0
- clm/layer2/engine/__init__.py +1 -0
- clm/layer2/engine/abstract_sql_engine.py +320 -0
- clm/layer2/engine/base.py +193 -0
- clm/layer2/engine/duckdb_engine.py +576 -0
- clm/layer2/engine/sqlite_engine.py +518 -0
- clm/layer2/fiber/__init__.py +31 -0
- clm/layer2/fiber/context.py +89 -0
- clm/layer2/fiber/disposable.py +163 -0
- clm/layer2/fiber/fiber.py +125 -0
- clm/layer2/fiber/hoare.py +191 -0
- clm/layer2/file_io.py +376 -0
- clm/layer2/file_registrar.py +77 -0
- clm/layer2/ingest.py +202 -0
- clm/layer2/lineage.py +322 -0
- clm/layer2/mcard_fs.py +262 -0
- clm/layer2/merkle.py +11 -0
- clm/layer2/mime.py +244 -0
- clm/layer2/mime_detector.py +245 -0
- clm/layer2/private_collection.py +118 -0
- clm/layer2/shared_store.py +11 -0
- clm/layer2/storage/__init__.py +190 -0
- clm/layer2/storage/db_bitemporal.py +141 -0
- clm/layer2/storage/db_driver.py +331 -0
- clm/layer2/storage/db_hyperlinks.py +214 -0
- clm/layer2/storage/db_pool.py +139 -0
- clm/layer2/storage/db_repository.py +229 -0
- clm/layer2/storage/savepoint.py +78 -0
- clm/layer2/storage/sqlite_wal.py +74 -0
- clm/layer2/tridb.py +245 -0
- clm/layer2/vcard_receipt.py +71 -0
- clm/layer2/vfs.py +17 -0
- clm/layer3/__init__.py +141 -0
- clm/layer3/agency/__init__.py +19 -0
- clm/layer3/agency/config.py +220 -0
- clm/layer3/agency/providers/__init__.py +15 -0
- clm/layer3/agency/providers/base.py +91 -0
- clm/layer3/agency/providers/mlc_llm.py +169 -0
- clm/layer3/agency/providers/ollama.py +203 -0
- clm/layer3/agency/router.py +71 -0
- clm/layer3/agency/runtime.py +524 -0
- clm/layer3/gateway.py +353 -0
- clm/layer3/open_interpreter_adapter.py +159 -0
- clm/layer3/rag/__init__.py +70 -0
- clm/layer3/rag/cli.py +228 -0
- clm/layer3/rag/config.py +153 -0
- clm/layer3/rag/embeddings/__init__.py +16 -0
- clm/layer3/rag/embeddings/base.py +79 -0
- clm/layer3/rag/embeddings/ollama.py +211 -0
- clm/layer3/rag/embeddings/vision.py +290 -0
- clm/layer3/rag/engine.py +276 -0
- clm/layer3/rag/graph/__init__.py +22 -0
- clm/layer3/rag/graph/community.py +198 -0
- clm/layer3/rag/graph/engine.py +433 -0
- clm/layer3/rag/graph/extractor.py +255 -0
- clm/layer3/rag/graph/schema.py +115 -0
- clm/layer3/rag/graph/store.py +641 -0
- clm/layer3/rag/indexer.py +333 -0
- clm/layer3/rag/llm_providers.json +52 -0
- clm/layer3/rag/semantic_versioning.py +313 -0
- clm/layer3/rag/vector/__init__.py +28 -0
- clm/layer3/rag/vector/handle_vector_store.py +749 -0
- clm/layer3/rag/vector/schema.py +151 -0
- clm/layer3/rag/vector/store.py +632 -0
- clm/layer3/reticulum/__init__.py +55 -0
- clm/layer3/reticulum/bridge.py +74 -0
- clm/layer3/reticulum/daemon.py +88 -0
- clm/layer3/reticulum/discovery.py +164 -0
- clm/layer3/reticulum/identity.py +107 -0
- clm/layer3/reticulum/media.py +148 -0
- clm/layer3/reticulum/transport.py +133 -0
- clm/layer3/satori/__init__.py +209 -0
- clm/layer3/satori/alpha_conversion.py +168 -0
- clm/layer3/satori/beta_reduction.py +359 -0
- clm/layer3/satori/continuation.py +38 -0
- clm/layer3/satori/eta_conversion.py +205 -0
- clm/layer3/satori/free_variables.py +163 -0
- clm/layer3/satori/io_effects.py +370 -0
- clm/layer3/satori/lambda_runtime.py +726 -0
- clm/layer3/satori/lambda_term.py +230 -0
- clm/layer3/satori/sheaf_audit.py +261 -0
- clm/layer3/satori/speech_act.py +279 -0
- clm/layer4/README.md +9 -0
- clm/layer4/__init__.py +67 -0
- clm/layer4/_sovereign_executor.py +580 -0
- clm/layer4/action_commands.py +719 -0
- clm/layer4/api.py +236 -0
- clm/layer4/bootstrap/__init__.py +13 -0
- clm/layer4/bootstrap/bootstrapper.py +118 -0
- clm/layer4/bootstrap/cleanup.py +44 -0
- clm/layer4/bootstrap/clm_hooks.py +49 -0
- clm/layer4/bootstrap/genesis.py +77 -0
- clm/layer4/bootstrap/injection.py +125 -0
- clm/layer4/cli.py +549 -0
- clm/layer4/collection_manager.py +553 -0
- clm/layer4/commands/__init__.py +81 -0
- clm/layer4/commands/action_cmd.py +42 -0
- clm/layer4/commands/check_cmd.py +82 -0
- clm/layer4/commands/envelope.py +73 -0
- clm/layer4/commands/evaluate_cmd.py +224 -0
- clm/layer4/commands/mcard_cmd.py +24 -0
- clm/layer4/commands/petri_cmd.py +92 -0
- clm/layer4/commands/tridb_cmd.py +319 -0
- clm/layer4/commands/version_cmd.py +27 -0
- clm/layer4/diff_commands.py +167 -0
- clm/layer4/eoa_sponsor.py +232 -0
- clm/layer4/events.py +69 -0
- clm/layer4/flux_gateway.py +133 -0
- clm/layer4/gateway.py +91 -0
- clm/layer4/loader.py +307 -0
- clm/layer4/mcard_commands.py +166 -0
- clm/layer4/membrane.py +273 -0
- clm/layer4/navigation.py +50 -0
- clm/layer4/protocol/protocol_entry_python.py +108 -0
- clm/layer4/protocol/python_sync.py +126 -0
- clm/layer4/runner.py +150 -0
- clm/layer4/satori.py +262 -0
- clm/layer4/server/python_sync_client.py +150 -0
- clm/layer4/server/python_sync_server.py +139 -0
- clm/layer4/server/service_api.py +315 -0
- clm/layer4/server/start_servers.py +107 -0
- clm/layer4/server/stop_servers.py +24 -0
- clm/layer4/simulated_optimization_fix.py +0 -0
- clm/layer4/storage.py +463 -0
- clm/layer4/tridb_admin.py +268 -0
- clm/layer4/websocket/test_ws_client.py +104 -0
- clm/layer4/websocket/test_ws_server.py +138 -0
- clm/layer4/websocket/ws_server_local.py +160 -0
- clm/layer5/__init__.py +51 -0
- clm/layer5/async_batch_writer.py +133 -0
- clm/layer5/learning.py +276 -0
- clm/layer5/telemetry_hook.py +107 -0
- clm/layer5/type_lattice.py +238 -0
- clm/py.typed +0 -0
- clm/schemas/__init__.py +652 -0
- clm/schemas/abstract_face_schema.yaml +77 -0
- clm/schemas/cel_schema.yaml +416 -0
- clm/schemas/lifecycle/__init__.py +45 -0
- clm/schemas/lifecycle/models.py +123 -0
- clm/schemas/lifecycle/service.py +364 -0
- clm/schemas/mcard_schema.sql +192 -0
- clm/schemas/mcard_vector_schema.sql +283 -0
- clm/schemas/mlp-context-v1.json +91 -0
- clm_kernel-0.0.0.dist-info/METADATA +121 -0
- clm_kernel-0.0.0.dist-info/RECORD +333 -0
- clm_kernel-0.0.0.dist-info/WHEEL +4 -0
- clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
- 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
|
+
]
|