andnp-searchkernel 0.1.0__tar.gz
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.
- andnp_searchkernel-0.1.0/.github/workflows/ci.yml +40 -0
- andnp_searchkernel-0.1.0/.github/workflows/publish.yml +32 -0
- andnp_searchkernel-0.1.0/.gitignore +54 -0
- andnp_searchkernel-0.1.0/LICENSE +21 -0
- andnp_searchkernel-0.1.0/PKG-INFO +34 -0
- andnp_searchkernel-0.1.0/README.md +11 -0
- andnp_searchkernel-0.1.0/pyproject.toml +79 -0
- andnp_searchkernel-0.1.0/searchkernel/__init__.py +19 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/cache/__init__.py +6 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/cache/memory_lru.py +71 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/cache/sqlite.py +113 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/embedding/__init__.py +5 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/embedding/huggingface.py +89 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/llm/__init__.py +5 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/llm/copilot.py +120 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/rerank/__init__.py +5 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/rerank/huggingface.py +158 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/stores/__init__.py +19 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/stores/pgvector.py +747 -0
- andnp_searchkernel-0.1.0/searchkernel/adapters/stores/pgvector_index.py +375 -0
- andnp_searchkernel-0.1.0/searchkernel/chunking/__init__.py +6 -0
- andnp_searchkernel-0.1.0/searchkernel/chunking/base.py +9 -0
- andnp_searchkernel-0.1.0/searchkernel/chunking/factory.py +7 -0
- andnp_searchkernel-0.1.0/searchkernel/chunking/header_chunker.py +668 -0
- andnp_searchkernel-0.1.0/searchkernel/compression/__init__.py +5 -0
- andnp_searchkernel-0.1.0/searchkernel/compression/thresholding.py +5 -0
- andnp_searchkernel-0.1.0/searchkernel/domain/__init__.py +41 -0
- andnp_searchkernel-0.1.0/searchkernel/domain/graph_policy.py +40 -0
- andnp_searchkernel-0.1.0/searchkernel/domain/models.py +374 -0
- andnp_searchkernel-0.1.0/searchkernel/domain/status_policy.py +34 -0
- andnp_searchkernel-0.1.0/searchkernel/domain/timestamp_policy.py +36 -0
- andnp_searchkernel-0.1.0/searchkernel/embeddings.py +53 -0
- andnp_searchkernel-0.1.0/searchkernel/eval/__init__.py +1 -0
- andnp_searchkernel-0.1.0/searchkernel/eval/golden.py +118 -0
- andnp_searchkernel-0.1.0/searchkernel/eval/metrics.py +131 -0
- andnp_searchkernel-0.1.0/searchkernel/eval/runner.py +259 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/bootstrap_checkpoint.py +409 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/bootstrap_snapshot.py +115 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/core.py +181 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/discovery.py +169 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/embedding_cache.py +206 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/git_ingestion.py +56 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/implicit_graph.py +156 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/manifest.py +65 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/migration.py +116 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/reconciler.py +179 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/runtime_readiness.py +134 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/semantic.py +288 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/stages.py +204 -0
- andnp_searchkernel-0.1.0/searchkernel/indexing/submission.py +60 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/__init__.py +1 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/_corruption.py +26 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/_writer_utils.py +22 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/graph.py +850 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/hash_store.py +151 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/keyword.py +874 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/protocol.py +29 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/vector.py +1942 -0
- andnp_searchkernel-0.1.0/searchkernel/indices/vocabulary_state.py +147 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/__init__.py +11 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/default_query_spec.py +54 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/executor.py +63 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/registry.py +165 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/spec.py +32 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stage.py +57 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/__init__.py +12 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/apply_move.py +138 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/calibrate.py +34 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/chunk.py +41 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/community_boost.py +74 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/dedup_content_hash.py +30 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/dedup_ngram.py +38 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/dedup_rerank.py +129 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/dedup_similarity.py +40 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/detect_moves.py +80 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/discover.py +67 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/doc_limit.py +31 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/effective_top_k.py +75 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/fusion.py +40 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/graph_expand.py +62 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/hydrate.py +86 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/index.py +68 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/parent_expansion.py +89 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/project_filter.py +54 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/project_uplift.py +68 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/provenance.py +59 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/rag_fusion.py +100 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/recency_boost.py +42 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/repair.py +52 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/rerank.py +52 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/retrieve.py +66 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/routing.py +52 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/rrf_fuse.py +27 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/seed_bookkeeping.py +127 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/source_filter.py +48 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/strategy_results.py +78 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/tag_expansion.py +95 -0
- andnp_searchkernel-0.1.0/searchkernel/pipeline/stages/threshold.py +27 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/__init__.py +39 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/chunking_config.py +20 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/content_source.py +85 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/embedding.py +43 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/index_manager.py +27 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/llm.py +40 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/orchestrator_config.py +40 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/rerank.py +34 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/search.py +46 -0
- andnp_searchkernel-0.1.0/searchkernel/ports/stores.py +177 -0
- andnp_searchkernel-0.1.0/searchkernel/runtime/__init__.py +13 -0
- andnp_searchkernel-0.1.0/searchkernel/runtime/cache.py +133 -0
- andnp_searchkernel-0.1.0/searchkernel/runtime/fanout.py +75 -0
- andnp_searchkernel-0.1.0/searchkernel/runtime/federation.py +84 -0
- andnp_searchkernel-0.1.0/searchkernel/runtime/query_embedding_cache.py +148 -0
- andnp_searchkernel-0.1.0/searchkernel/runtime/registry.py +39 -0
- andnp_searchkernel-0.1.0/searchkernel/runtime/reindex.py +238 -0
- andnp_searchkernel-0.1.0/searchkernel/runtime/trace.py +119 -0
- andnp_searchkernel-0.1.0/searchkernel/search/__init__.py +24 -0
- andnp_searchkernel-0.1.0/searchkernel/search/base_orchestrator.py +140 -0
- andnp_searchkernel-0.1.0/searchkernel/search/calibration.py +26 -0
- andnp_searchkernel-0.1.0/searchkernel/search/chunk_hydrator.py +317 -0
- andnp_searchkernel-0.1.0/searchkernel/search/classifier.py +104 -0
- andnp_searchkernel-0.1.0/searchkernel/search/community.py +29 -0
- andnp_searchkernel-0.1.0/searchkernel/search/dedup.py +134 -0
- andnp_searchkernel-0.1.0/searchkernel/search/edge_types.py +57 -0
- andnp_searchkernel-0.1.0/searchkernel/search/filters.py +48 -0
- andnp_searchkernel-0.1.0/searchkernel/search/fusion.py +34 -0
- andnp_searchkernel-0.1.0/searchkernel/search/graph_expansion.py +80 -0
- andnp_searchkernel-0.1.0/searchkernel/search/hyde.py +32 -0
- andnp_searchkernel-0.1.0/searchkernel/search/normalization.py +29 -0
- andnp_searchkernel-0.1.0/searchkernel/search/orchestrator.py +756 -0
- andnp_searchkernel-0.1.0/searchkernel/search/path_utils.py +186 -0
- andnp_searchkernel-0.1.0/searchkernel/search/pipeline.py +129 -0
- andnp_searchkernel-0.1.0/searchkernel/search/query_execution.py +119 -0
- andnp_searchkernel-0.1.0/searchkernel/search/reranker.py +126 -0
- andnp_searchkernel-0.1.0/searchkernel/search/result_cache.py +47 -0
- andnp_searchkernel-0.1.0/searchkernel/search/score_pipeline.py +142 -0
- andnp_searchkernel-0.1.0/searchkernel/search/tag_expansion.py +173 -0
- andnp_searchkernel-0.1.0/searchkernel/search/time_scoring.py +100 -0
- andnp_searchkernel-0.1.0/searchkernel/search/types.py +12 -0
- andnp_searchkernel-0.1.0/searchkernel/search/utils.py +55 -0
- andnp_searchkernel-0.1.0/searchkernel/search/variance.py +42 -0
- andnp_searchkernel-0.1.0/searchkernel/storage/__init__.py +0 -0
- andnp_searchkernel-0.1.0/searchkernel/storage/db.py +100 -0
- andnp_searchkernel-0.1.0/searchkernel/utils/__init__.py +58 -0
- andnp_searchkernel-0.1.0/searchkernel/utils/atomic_io.py +73 -0
- andnp_searchkernel-0.1.0/searchkernel/utils/circuit_breaker.py +207 -0
- andnp_searchkernel-0.1.0/searchkernel/utils/similarity.py +24 -0
- andnp_searchkernel-0.1.0/tests/__init__.py +1 -0
- andnp_searchkernel-0.1.0/tests/conftest.py +343 -0
- andnp_searchkernel-0.1.0/tests/integration/__init__.py +0 -0
- andnp_searchkernel-0.1.0/tests/integration/conftest.py +35 -0
- andnp_searchkernel-0.1.0/tests/integration/test_hf_embedding_pgvector.py +107 -0
- andnp_searchkernel-0.1.0/tests/integration/test_pgvector_index.py +237 -0
- andnp_searchkernel-0.1.0/tests/integration/test_pgvector_store.py +486 -0
- andnp_searchkernel-0.1.0/tests/regression/__init__.py +0 -0
- andnp_searchkernel-0.1.0/tests/unit/__init__.py +0 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/__init__.py +0 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/__init__.py +0 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_apply_move.py +146 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_calibrate.py +31 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_community_boost.py +73 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_dedup_content_hash.py +41 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_dedup_ngram.py +52 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_dedup_rerank.py +64 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_dedup_similarity.py +54 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_detect_moves.py +89 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_discover.py +65 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_doc_limit.py +36 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_effective_top_k.py +89 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_fusion.py +42 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_graph_expand.py +80 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_hydrate.py +84 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_index.py +72 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_project_filter.py +44 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_project_uplift.py +60 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_provenance.py +73 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_rag_fusion.py +96 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_recency_boost.py +66 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_repair.py +66 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_rerank.py +81 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_retrieve.py +86 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_routing.py +47 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_rrf_fuse.py +39 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_seed_bookkeeping.py +75 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_source_filter.py +36 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_strategy_results.py +61 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_tag_expansion.py +108 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/stages/test_threshold.py +31 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/test_default_query_spec.py +27 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/test_executor.py +94 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/test_registry.py +228 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/test_spec.py +27 -0
- andnp_searchkernel-0.1.0/tests/unit/pipeline/test_stage.py +60 -0
- andnp_searchkernel-0.1.0/tests/unit/search/__init__.py +0 -0
- andnp_searchkernel-0.1.0/tests/unit/search/test_graph_expansion.py +62 -0
- andnp_searchkernel-0.1.0/tests/unit/test_atomic_io.py +190 -0
- andnp_searchkernel-0.1.0/tests/unit/test_bootstrap_checkpoint.py +91 -0
- andnp_searchkernel-0.1.0/tests/unit/test_bootstrap_checkpoint_semantic.py +269 -0
- andnp_searchkernel-0.1.0/tests/unit/test_bootstrap_snapshot.py +171 -0
- andnp_searchkernel-0.1.0/tests/unit/test_cache_store_adapters.py +232 -0
- andnp_searchkernel-0.1.0/tests/unit/test_cached_decorator.py +262 -0
- andnp_searchkernel-0.1.0/tests/unit/test_calibration.py +258 -0
- andnp_searchkernel-0.1.0/tests/unit/test_chunk_hashing.py +115 -0
- andnp_searchkernel-0.1.0/tests/unit/test_classifier.py +93 -0
- andnp_searchkernel-0.1.0/tests/unit/test_community.py +177 -0
- andnp_searchkernel-0.1.0/tests/unit/test_copilot_llm_provider.py +178 -0
- andnp_searchkernel-0.1.0/tests/unit/test_corruption_detection.py +17 -0
- andnp_searchkernel-0.1.0/tests/unit/test_cross_process.py +108 -0
- andnp_searchkernel-0.1.0/tests/unit/test_db_schema.py +280 -0
- andnp_searchkernel-0.1.0/tests/unit/test_dedup.py +605 -0
- andnp_searchkernel-0.1.0/tests/unit/test_edge_types.py +274 -0
- andnp_searchkernel-0.1.0/tests/unit/test_eval_golden.py +242 -0
- andnp_searchkernel-0.1.0/tests/unit/test_eval_metrics.py +242 -0
- andnp_searchkernel-0.1.0/tests/unit/test_eval_runner.py +182 -0
- andnp_searchkernel-0.1.0/tests/unit/test_fanout.py +248 -0
- andnp_searchkernel-0.1.0/tests/unit/test_federation.py +212 -0
- andnp_searchkernel-0.1.0/tests/unit/test_file_discovery.py +134 -0
- andnp_searchkernel-0.1.0/tests/unit/test_filters.py +205 -0
- andnp_searchkernel-0.1.0/tests/unit/test_fusion.py +112 -0
- andnp_searchkernel-0.1.0/tests/unit/test_fusion_scoring.py +234 -0
- andnp_searchkernel-0.1.0/tests/unit/test_graph_corruption.py +201 -0
- andnp_searchkernel-0.1.0/tests/unit/test_graph_policy.py +21 -0
- andnp_searchkernel-0.1.0/tests/unit/test_graph_store.py +398 -0
- andnp_searchkernel-0.1.0/tests/unit/test_hash_store.py +290 -0
- andnp_searchkernel-0.1.0/tests/unit/test_hf_embedding_provider.py +66 -0
- andnp_searchkernel-0.1.0/tests/unit/test_hf_reranker.py +94 -0
- andnp_searchkernel-0.1.0/tests/unit/test_hyde.py +253 -0
- andnp_searchkernel-0.1.0/tests/unit/test_indexing_stages.py +329 -0
- andnp_searchkernel-0.1.0/tests/unit/test_keyword_exclusion.py +128 -0
- andnp_searchkernel-0.1.0/tests/unit/test_keyword_index.py +1185 -0
- andnp_searchkernel-0.1.0/tests/unit/test_lazy_loading.py +93 -0
- andnp_searchkernel-0.1.0/tests/unit/test_legacy_migration.py +165 -0
- andnp_searchkernel-0.1.0/tests/unit/test_manifest.py +170 -0
- andnp_searchkernel-0.1.0/tests/unit/test_manifest_corruption.py +242 -0
- andnp_searchkernel-0.1.0/tests/unit/test_manifest_migration.py +282 -0
- andnp_searchkernel-0.1.0/tests/unit/test_path_utils.py +340 -0
- andnp_searchkernel-0.1.0/tests/unit/test_query_embedding_cache.py +123 -0
- andnp_searchkernel-0.1.0/tests/unit/test_query_expansion.py +467 -0
- andnp_searchkernel-0.1.0/tests/unit/test_reconciler.py +466 -0
- andnp_searchkernel-0.1.0/tests/unit/test_reranker.py +311 -0
- andnp_searchkernel-0.1.0/tests/unit/test_reranker_circuit_breaker.py +228 -0
- andnp_searchkernel-0.1.0/tests/unit/test_runtime_readiness.py +83 -0
- andnp_searchkernel-0.1.0/tests/unit/test_runtime_trace.py +185 -0
- andnp_searchkernel-0.1.0/tests/unit/test_score_pipeline.py +526 -0
- andnp_searchkernel-0.1.0/tests/unit/test_search_utils.py +92 -0
- andnp_searchkernel-0.1.0/tests/unit/test_semantic_indexing.py +419 -0
- andnp_searchkernel-0.1.0/tests/unit/test_semantic_tier_from_progress.py +59 -0
- andnp_searchkernel-0.1.0/tests/unit/test_similarity.py +20 -0
- andnp_searchkernel-0.1.0/tests/unit/test_source_registry.py +89 -0
- andnp_searchkernel-0.1.0/tests/unit/test_status_policy.py +25 -0
- andnp_searchkernel-0.1.0/tests/unit/test_time_scoring.py +361 -0
- andnp_searchkernel-0.1.0/tests/unit/test_timestamp_policy.py +30 -0
- andnp_searchkernel-0.1.0/tests/unit/test_variance.py +329 -0
- andnp_searchkernel-0.1.0/tests/unit/test_vector_circuit_breaker.py +531 -0
- andnp_searchkernel-0.1.0/tests/unit/test_vector_corruption.py +328 -0
- andnp_searchkernel-0.1.0/tests/unit/test_vector_exclusion.py +128 -0
- andnp_searchkernel-0.1.0/tests/unit/test_vector_parallel_embeddings.py +343 -0
- andnp_searchkernel-0.1.0/tests/unit/test_vector_race_condition.py +218 -0
- andnp_searchkernel-0.1.0/tests/unit/test_vector_sqlite.py +160 -0
- andnp_searchkernel-0.1.0/tests/unit/test_vector_threading.py +25 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint-and-test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v3
|
|
26
|
+
|
|
27
|
+
- name: Sync dependencies
|
|
28
|
+
run: uv sync
|
|
29
|
+
|
|
30
|
+
- name: Lint with ruff
|
|
31
|
+
run: uv run ruff check .
|
|
32
|
+
|
|
33
|
+
- name: Type check
|
|
34
|
+
run: uv run pyright
|
|
35
|
+
|
|
36
|
+
- name: Check imports with import-linter
|
|
37
|
+
run: uv run lint-imports
|
|
38
|
+
|
|
39
|
+
- name: Run unit tests
|
|
40
|
+
run: uv run pytest -m unit -v
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
name: Publish to PyPI
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v3
|
|
25
|
+
|
|
26
|
+
- name: Build package
|
|
27
|
+
run: uv build
|
|
28
|
+
|
|
29
|
+
- name: Publish to PyPI
|
|
30
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
31
|
+
with:
|
|
32
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
pip-wheel-metadata/
|
|
20
|
+
share/python-wheels/
|
|
21
|
+
*.egg-info/
|
|
22
|
+
.installed.cfg
|
|
23
|
+
*.egg
|
|
24
|
+
MANIFEST
|
|
25
|
+
|
|
26
|
+
# Virtual environments
|
|
27
|
+
.venv/
|
|
28
|
+
venv/
|
|
29
|
+
ENV/
|
|
30
|
+
env/
|
|
31
|
+
.env
|
|
32
|
+
|
|
33
|
+
# IDE
|
|
34
|
+
.vscode/
|
|
35
|
+
.idea/
|
|
36
|
+
*.swp
|
|
37
|
+
*.swo
|
|
38
|
+
*~
|
|
39
|
+
.DS_Store
|
|
40
|
+
|
|
41
|
+
# Testing
|
|
42
|
+
.pytest_cache/
|
|
43
|
+
.coverage
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.hypothesis/
|
|
47
|
+
|
|
48
|
+
# Tools
|
|
49
|
+
.ruff_cache/
|
|
50
|
+
.mypy_cache/
|
|
51
|
+
.pytype/
|
|
52
|
+
|
|
53
|
+
# Lock files (generated by uv)
|
|
54
|
+
uv.lock
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 andnp-searchkernel contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: andnp-searchkernel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Source-agnostic search/indexing kernel: domain-agnostic content ingestion, hybrid vector+keyword+graph search, pluggable embedding/LLM/reranker providers.
|
|
5
|
+
Project-URL: Homepage, https://github.com/andnp/andnp-searchkernel
|
|
6
|
+
Project-URL: Documentation, https://github.com/andnp/andnp-searchkernel/tree/main/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/andnp/andnp-searchkernel.git
|
|
8
|
+
Project-URL: Issues, https://github.com/andnp/andnp-searchkernel/issues
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.13
|
|
11
|
+
Requires-Dist: faiss-cpu>=1.9.0
|
|
12
|
+
Requires-Dist: llama-index-core>=0.12.0
|
|
13
|
+
Requires-Dist: llama-index-embeddings-huggingface>=0.4.0
|
|
14
|
+
Requires-Dist: llama-index-vector-stores-faiss>=0.3.0
|
|
15
|
+
Requires-Dist: psycopg2-binary>=2.9.0
|
|
16
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
17
|
+
Requires-Dist: sentence-transformers>=3.0.0
|
|
18
|
+
Requires-Dist: tenacity>=9.0.0
|
|
19
|
+
Requires-Dist: tomlkit>=0.13.0
|
|
20
|
+
Requires-Dist: tree-sitter-markdown>=0.3.2
|
|
21
|
+
Requires-Dist: tree-sitter>=0.23.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# `andnp-searchkernel`
|
|
25
|
+
|
|
26
|
+
A domain-agnostic search/indexing kernel for building hybrid vector + keyword + graph search systems with pluggable embedding, LLM, and reranker providers.
|
|
27
|
+
|
|
28
|
+
## Status
|
|
29
|
+
|
|
30
|
+
**Pre-alpha, extraction in progress.** This library is being extracted from [`mcp-markdown-ragdocs`](https://github.com/andnp/mcp-markdown-ragdocs) to enable reuse across arbitrary content sources and search backends.
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# `andnp-searchkernel`
|
|
2
|
+
|
|
3
|
+
A domain-agnostic search/indexing kernel for building hybrid vector + keyword + graph search systems with pluggable embedding, LLM, and reranker providers.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
**Pre-alpha, extraction in progress.** This library is being extracted from [`mcp-markdown-ragdocs`](https://github.com/andnp/mcp-markdown-ragdocs) to enable reuse across arbitrary content sources and search backends.
|
|
8
|
+
|
|
9
|
+
## License
|
|
10
|
+
|
|
11
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "andnp-searchkernel"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Source-agnostic search/indexing kernel: domain-agnostic content ingestion, hybrid vector+keyword+graph search, pluggable embedding/LLM/reranker providers."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"llama-index-core>=0.12.0",
|
|
13
|
+
"llama-index-embeddings-huggingface>=0.4.0",
|
|
14
|
+
"llama-index-vector-stores-faiss>=0.3.0",
|
|
15
|
+
"faiss-cpu>=1.9.0",
|
|
16
|
+
"sentence-transformers>=3.0.0",
|
|
17
|
+
"tenacity>=9.0.0",
|
|
18
|
+
"psycopg2-binary>=2.9.0",
|
|
19
|
+
"pyyaml>=6.0.2",
|
|
20
|
+
"tomlkit>=0.13.0",
|
|
21
|
+
"tree-sitter>=0.23.0",
|
|
22
|
+
"tree-sitter-markdown>=0.3.2",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/andnp/andnp-searchkernel"
|
|
27
|
+
Documentation = "https://github.com/andnp/andnp-searchkernel/tree/main/docs"
|
|
28
|
+
Repository = "https://github.com/andnp/andnp-searchkernel.git"
|
|
29
|
+
Issues = "https://github.com/andnp/andnp-searchkernel/issues"
|
|
30
|
+
|
|
31
|
+
[dependency-groups]
|
|
32
|
+
dev = [
|
|
33
|
+
"pyright>=1.1.407",
|
|
34
|
+
"pytest>=9.0.2",
|
|
35
|
+
"pytest-asyncio>=0.24.0",
|
|
36
|
+
"pytest-cov>=6.0.0",
|
|
37
|
+
"pytest-xdist>=3.5.0",
|
|
38
|
+
"ruff>=0.14.10",
|
|
39
|
+
"ty>=0.0.12",
|
|
40
|
+
"import-linter>=2.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.uv]
|
|
44
|
+
package = true
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["searchkernel"]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
pythonpath = "."
|
|
51
|
+
addopts = "-n 4 --dist worksteal"
|
|
52
|
+
markers = [
|
|
53
|
+
"serial: marks tests that must run serially (not in parallel)",
|
|
54
|
+
"unit: marks unit tests (fast, isolated)",
|
|
55
|
+
"integration: marks integration tests (involve multiple components)",
|
|
56
|
+
"e2e: marks end-to-end tests (full system tests)",
|
|
57
|
+
"slow: marks slow tests (>5s, load models or process large datasets)",
|
|
58
|
+
"real_embeddings: marks tests that require real embedding models (not fake deterministic embeddings)",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.importlinter]
|
|
62
|
+
root_packages = ["searchkernel"]
|
|
63
|
+
default_mapping = "searchkernel"
|
|
64
|
+
|
|
65
|
+
[[tool.importlinter.contracts]]
|
|
66
|
+
name = "domain and ports are inward-only"
|
|
67
|
+
type = "forbidden"
|
|
68
|
+
source_modules = ["searchkernel.domain", "searchkernel.ports"]
|
|
69
|
+
forbidden_modules = [
|
|
70
|
+
"searchkernel.adapters",
|
|
71
|
+
"searchkernel.pipeline",
|
|
72
|
+
"searchkernel.search",
|
|
73
|
+
"searchkernel.indices",
|
|
74
|
+
"searchkernel.storage",
|
|
75
|
+
"searchkernel.runtime",
|
|
76
|
+
"searchkernel.eval",
|
|
77
|
+
"searchkernel.compression",
|
|
78
|
+
]
|
|
79
|
+
ignore_imports = []
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Source-agnostic search/indexing kernel.
|
|
3
|
+
|
|
4
|
+
A domain-agnostic library for building hybrid vector+keyword+graph search systems
|
|
5
|
+
with pluggable embedding/LLM/reranker providers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from searchkernel.search.orchestrator import SearchOrchestrator
|
|
9
|
+
from searchkernel.search.pipeline import SearchPipelineConfig
|
|
10
|
+
from searchkernel.search.utils import classify_query_type, truncate_content
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"SearchOrchestrator",
|
|
16
|
+
"SearchPipelineConfig",
|
|
17
|
+
"classify_query_type",
|
|
18
|
+
"truncate_content",
|
|
19
|
+
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""In-memory LRU cache store with epoch-based invalidation."""
|
|
2
|
+
|
|
3
|
+
from collections import OrderedDict
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MemoryLRUCacheStore:
|
|
8
|
+
"""LRU in-memory cache store implementing the CacheStore port.
|
|
9
|
+
|
|
10
|
+
Stores key-value pairs in memory with a bounded size. When capacity is
|
|
11
|
+
exceeded, the least recently used entry is evicted. Entries are tagged
|
|
12
|
+
with an epoch for invalidation support.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, max_entries: int = 128):
|
|
16
|
+
"""Initialize memory LRU cache store.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
max_entries: Maximum number of entries to store (default 128).
|
|
20
|
+
Must be at least 1.
|
|
21
|
+
"""
|
|
22
|
+
self._max_entries = max(1, max_entries)
|
|
23
|
+
self._entries: OrderedDict[str, tuple[Any, int]] = OrderedDict()
|
|
24
|
+
|
|
25
|
+
def get(self, key: str) -> Any | None:
|
|
26
|
+
"""Retrieve a cached value.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
key: Cache key.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Cached value, or None if not found.
|
|
33
|
+
"""
|
|
34
|
+
if key not in self._entries:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
value, _epoch = self._entries[key]
|
|
38
|
+
# Move to end to mark as recently used (LRU)
|
|
39
|
+
self._entries.move_to_end(key)
|
|
40
|
+
return value
|
|
41
|
+
|
|
42
|
+
def set(self, key: str, value: Any, epoch: int) -> None:
|
|
43
|
+
"""Store a value with an associated epoch.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
key: Cache key.
|
|
47
|
+
value: Value to cache.
|
|
48
|
+
epoch: Index epoch at cache time. Used for invalidation.
|
|
49
|
+
"""
|
|
50
|
+
if key in self._entries:
|
|
51
|
+
self._entries.pop(key)
|
|
52
|
+
|
|
53
|
+
self._entries[key] = (value, epoch)
|
|
54
|
+
self._entries.move_to_end(key)
|
|
55
|
+
|
|
56
|
+
# Evict LRU entry if over capacity
|
|
57
|
+
while len(self._entries) > self._max_entries:
|
|
58
|
+
self._entries.popitem(last=False)
|
|
59
|
+
|
|
60
|
+
def invalidate_epoch(self, epoch: int) -> None:
|
|
61
|
+
"""Invalidate all entries from an epoch or earlier.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
epoch: Entries with epoch <= this are discarded.
|
|
65
|
+
"""
|
|
66
|
+
keys_to_delete = [
|
|
67
|
+
key for key, (_, entry_epoch) in self._entries.items()
|
|
68
|
+
if entry_epoch <= epoch
|
|
69
|
+
]
|
|
70
|
+
for key in keys_to_delete:
|
|
71
|
+
del self._entries[key]
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""SQLite-backed cache store with epoch-based invalidation."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import sqlite3
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SQLiteCacheStore:
|
|
13
|
+
"""SQLite-backed cache store implementing the CacheStore port.
|
|
14
|
+
|
|
15
|
+
Persists key-value pairs to a SQLite database. Entries are tagged with
|
|
16
|
+
an epoch for invalidation support. Designed for durability across restarts.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, db_path: Path | str):
|
|
20
|
+
"""Initialize SQLite cache store.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
db_path: Path to SQLite database file.
|
|
24
|
+
"""
|
|
25
|
+
self.db_path = Path(db_path)
|
|
26
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
27
|
+
self._init_schema()
|
|
28
|
+
|
|
29
|
+
def _init_schema(self) -> None:
|
|
30
|
+
"""Create the cache table if it doesn't exist."""
|
|
31
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
32
|
+
conn.execute("""
|
|
33
|
+
CREATE TABLE IF NOT EXISTS cache_store (
|
|
34
|
+
key TEXT PRIMARY KEY,
|
|
35
|
+
value TEXT NOT NULL,
|
|
36
|
+
epoch INTEGER NOT NULL,
|
|
37
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
38
|
+
);
|
|
39
|
+
""")
|
|
40
|
+
conn.execute("""
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_cache_epoch
|
|
42
|
+
ON cache_store (epoch);
|
|
43
|
+
""")
|
|
44
|
+
conn.commit()
|
|
45
|
+
|
|
46
|
+
def get(self, key: str) -> Any | None:
|
|
47
|
+
"""Retrieve a cached value.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
key: Cache key.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Cached value, or None if not found.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
57
|
+
cursor = conn.execute(
|
|
58
|
+
"SELECT value FROM cache_store WHERE key = ?;",
|
|
59
|
+
(key,),
|
|
60
|
+
)
|
|
61
|
+
row = cursor.fetchone()
|
|
62
|
+
if row:
|
|
63
|
+
value_json = row[0]
|
|
64
|
+
return json.loads(value_json)
|
|
65
|
+
return None
|
|
66
|
+
except (sqlite3.DatabaseError, json.JSONDecodeError) as e:
|
|
67
|
+
logger.warning(f"Error retrieving cache key {key}: {e}", exc_info=True)
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
def set(self, key: str, value: Any, epoch: int) -> None:
|
|
71
|
+
"""Store a value with an associated epoch.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
key: Cache key.
|
|
75
|
+
value: Value to cache.
|
|
76
|
+
epoch: Index epoch at cache time. Used for invalidation.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
value_json = json.dumps(value)
|
|
80
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
81
|
+
conn.execute(
|
|
82
|
+
"""
|
|
83
|
+
INSERT INTO cache_store (key, value, epoch)
|
|
84
|
+
VALUES (?, ?, ?)
|
|
85
|
+
ON CONFLICT (key) DO UPDATE SET
|
|
86
|
+
value = EXCLUDED.value,
|
|
87
|
+
epoch = EXCLUDED.epoch;
|
|
88
|
+
""",
|
|
89
|
+
(key, value_json, epoch),
|
|
90
|
+
)
|
|
91
|
+
conn.commit()
|
|
92
|
+
except (sqlite3.DatabaseError, TypeError):
|
|
93
|
+
logger.exception(f"Error storing cache key {key}")
|
|
94
|
+
|
|
95
|
+
def invalidate_epoch(self, epoch: int) -> None:
|
|
96
|
+
"""Invalidate all entries from an epoch or earlier.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
epoch: Entries with epoch <= this are discarded.
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
103
|
+
cursor = conn.execute(
|
|
104
|
+
"DELETE FROM cache_store WHERE epoch <= ?;",
|
|
105
|
+
(epoch,),
|
|
106
|
+
)
|
|
107
|
+
conn.commit()
|
|
108
|
+
logger.debug(
|
|
109
|
+
f"Invalidated cache entries for epochs <= {epoch} "
|
|
110
|
+
f"({cursor.rowcount} entries deleted)"
|
|
111
|
+
)
|
|
112
|
+
except sqlite3.DatabaseError:
|
|
113
|
+
logger.exception(f"Error invalidating cache epoch {epoch}")
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""HuggingFace / sentence-transformers EmbeddingProvider adapter.
|
|
2
|
+
|
|
3
|
+
In-process embedding via ``sentence_transformers.SentenceTransformer``.
|
|
4
|
+
Defaults to Qwen3-Embedding-0.6B. No Ollama, no external service.
|
|
5
|
+
|
|
6
|
+
This is an ADDITIVE port implementation. The live embedding path
|
|
7
|
+
(``searchkernel/indices/vector.py``, BAAI/bge-small-en-v1.5) is untouched.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
from searchkernel.domain import Vector
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from sentence_transformers import SentenceTransformer
|
|
18
|
+
|
|
19
|
+
# Documented Qwen3-Embedding query instruction, used only if the loaded model
|
|
20
|
+
# does not expose a "query" prompt via sentence-transformers.
|
|
21
|
+
_QWEN3_QUERY_INSTRUCTION = (
|
|
22
|
+
"Instruct: Given a web search query, retrieve relevant passages\nQuery: "
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class HuggingFaceEmbeddingProvider:
|
|
27
|
+
"""EmbeddingProvider backed by a sentence-transformers model.
|
|
28
|
+
|
|
29
|
+
Qwen3-Embedding is asymmetric: queries take an instruction prompt,
|
|
30
|
+
documents do not. ``embed`` embeds DOCUMENTS (no prompt); ``embed_query``
|
|
31
|
+
applies the query instruction. The EmbeddingProvider port itself has no
|
|
32
|
+
query variant yet -- that asymmetry lives here until W4a lifts it into
|
|
33
|
+
the port contract.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
model_name: str = "Qwen/Qwen3-Embedding-0.6B",
|
|
39
|
+
*,
|
|
40
|
+
truncate_dim: int | None = None,
|
|
41
|
+
device: str | None = None,
|
|
42
|
+
):
|
|
43
|
+
from sentence_transformers import SentenceTransformer
|
|
44
|
+
|
|
45
|
+
self.model_name = model_name
|
|
46
|
+
self._truncate_dim = truncate_dim
|
|
47
|
+
# Matryoshka (MRL): passing truncate_dim makes the model emit
|
|
48
|
+
# truncated + re-normalized vectors directly.
|
|
49
|
+
self._model: SentenceTransformer = SentenceTransformer(
|
|
50
|
+
model_name, truncate_dim=truncate_dim, device=device
|
|
51
|
+
)
|
|
52
|
+
native_dim = self._model.get_sentence_embedding_dimension()
|
|
53
|
+
self.dim: int = truncate_dim if truncate_dim is not None else int(native_dim)
|
|
54
|
+
# Whether the model ships a named "query" prompt we can reference.
|
|
55
|
+
self._has_query_prompt = "query" in getattr(self._model, "prompts", {})
|
|
56
|
+
|
|
57
|
+
def embed(self, texts: list[str]) -> list[Vector]:
|
|
58
|
+
"""Embed DOCUMENTS (no instruction prompt), L2-normalized."""
|
|
59
|
+
embeddings = self._model.encode(
|
|
60
|
+
texts,
|
|
61
|
+
batch_size=32,
|
|
62
|
+
normalize_embeddings=True,
|
|
63
|
+
convert_to_numpy=True,
|
|
64
|
+
)
|
|
65
|
+
return embeddings.tolist()
|
|
66
|
+
|
|
67
|
+
def embed_query(self, text: str) -> Vector:
|
|
68
|
+
"""Embed a single QUERY with the Qwen3 instruction prompt applied."""
|
|
69
|
+
return self.embed_queries([text])[0]
|
|
70
|
+
|
|
71
|
+
def embed_queries(self, texts: list[str]) -> list[Vector]:
|
|
72
|
+
"""Embed QUERIES with the Qwen3 instruction prompt applied."""
|
|
73
|
+
if self._has_query_prompt:
|
|
74
|
+
embeddings = self._model.encode(
|
|
75
|
+
texts,
|
|
76
|
+
prompt_name="query",
|
|
77
|
+
batch_size=32,
|
|
78
|
+
normalize_embeddings=True,
|
|
79
|
+
convert_to_numpy=True,
|
|
80
|
+
)
|
|
81
|
+
else:
|
|
82
|
+
embeddings = self._model.encode(
|
|
83
|
+
texts,
|
|
84
|
+
prompt=_QWEN3_QUERY_INSTRUCTION,
|
|
85
|
+
batch_size=32,
|
|
86
|
+
normalize_embeddings=True,
|
|
87
|
+
convert_to_numpy=True,
|
|
88
|
+
)
|
|
89
|
+
return embeddings.tolist()
|