mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
- Robust ID extraction in CentralityCalculator, CommunityDetector, and ConnectivityAnalyzer - Support for direct Entity objects and dictionaries as node identifiers - Improved Entity hashability in utils/types.py - Added integration test to verify fix and prevent regression
189 lines
7.1 KiB
Python
189 lines
7.1 KiB
Python
import unittest
|
|
import sys
|
|
import os
|
|
import numpy as np
|
|
|
|
import pytest
|
|
|
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
|
|
from semantica.embeddings import EmbeddingGenerator, TextEmbedder
|
|
from semantica.vector_store import (
|
|
VectorStore, FAISSStore, HybridSearch, MetadataFilter,
|
|
SearchRanker, NamespaceManager
|
|
)
|
|
from semantica.reasoning import Reasoner
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
class TestSemanticaFeatures(unittest.TestCase):
|
|
|
|
def test_01_embedding_generation(self):
|
|
"""Test basic embedding generation with default provider (Sentence Transformers)"""
|
|
print("\nTesting Embedding Generation...")
|
|
generator = EmbeddingGenerator()
|
|
texts = [
|
|
"Apple Inc. is a technology company.",
|
|
"Microsoft Corporation develops software.",
|
|
"Amazon provides cloud services."
|
|
]
|
|
embeddings = generator.generate_embeddings(texts, data_type="text")
|
|
|
|
self.assertEqual(len(embeddings), 3)
|
|
self.assertTrue(embeddings.shape[1] > 0)
|
|
print("Embedding Generation: OK")
|
|
|
|
def test_02_text_embedder(self):
|
|
"""Test TextEmbedder specific functionality"""
|
|
print("\nTesting TextEmbedder...")
|
|
text_embedder = TextEmbedder()
|
|
text = "Semantic knowledge graphs enable intelligent data processing."
|
|
embedding = text_embedder.embed_text(text)
|
|
|
|
self.assertTrue(len(embedding) > 0)
|
|
print("TextEmbedder: OK")
|
|
|
|
def test_03_model_switching(self):
|
|
"""Test dynamic model switching"""
|
|
print("\nTesting Dynamic Model Switching...")
|
|
embedder = TextEmbedder(method="sentence_transformers")
|
|
info = embedder.get_model_info()
|
|
self.assertEqual(info["method"], "sentence_transformers")
|
|
|
|
# Switch to FastEmbed
|
|
try:
|
|
print("Switching to FastEmbed...")
|
|
# Use a known small model for testing
|
|
embedder.set_model("fastembed", "BAAI/bge-small-en-v1.5")
|
|
info = embedder.get_model_info()
|
|
self.assertEqual(info["method"], "fastembed")
|
|
self.assertEqual(info["model_name"], "BAAI/bge-small-en-v1.5")
|
|
|
|
emb = embedder.embed_text("Test")
|
|
self.assertEqual(len(emb), 384) # BGE small is 384 dim
|
|
print("Switch to FastEmbed: OK")
|
|
except ImportError:
|
|
print("FastEmbed not installed, skipping switch test")
|
|
except Exception as e:
|
|
print(f"Switch failed: {e}")
|
|
# Do not fail test if model download fails (e.g. network issue), but log it
|
|
# But for this task we should probably expect it to work if dependencies are there
|
|
pass
|
|
|
|
def test_04_vector_store_basic(self):
|
|
"""Test VectorStore storage and search"""
|
|
print("\nTesting Vector Store Basic...")
|
|
store = VectorStore(backend="faiss", dimension=768)
|
|
|
|
# Store vectors
|
|
vectors = [np.random.rand(768).astype('float32') for _ in range(10)]
|
|
metadata = [{"id": i, "text": f"doc_{i}"} for i in range(10)]
|
|
|
|
vector_ids = store.store_vectors(vectors, metadata=metadata)
|
|
self.assertEqual(len(vector_ids), 10)
|
|
|
|
# Search
|
|
query = np.random.rand(768).astype('float32')
|
|
results = store.search_vectors(query, k=5)
|
|
self.assertEqual(len(results), 5)
|
|
print("VectorStore Basic: OK")
|
|
|
|
def test_05_faiss_store(self):
|
|
"""Test FAISSStore directly"""
|
|
print("\nTesting FAISSStore...")
|
|
store = FAISSStore(dimension=768)
|
|
index = store.create_index(index_type="hnsw", metric="L2", m=16)
|
|
|
|
vectors = np.random.rand(100, 768).astype('float32')
|
|
ids = [f"doc_{i}" for i in range(len(vectors))]
|
|
|
|
# Add vectors
|
|
store.add_vectors(vectors, ids=ids)
|
|
|
|
# Search
|
|
query = np.random.rand(768).astype('float32')
|
|
results = store.search_similar(query, k=5)
|
|
self.assertEqual(len(results), 5)
|
|
print("FAISSStore: OK")
|
|
|
|
def test_06_hybrid_search(self):
|
|
"""Test Hybrid Search with Metadata Filtering"""
|
|
print("\nTesting Hybrid Search...")
|
|
search = HybridSearch()
|
|
|
|
# Mock data
|
|
docs = [
|
|
{"id": 0, "category": "Tech", "year": 2024},
|
|
{"id": 1, "category": "Tech", "year": 2023},
|
|
{"id": 2, "category": "Biz", "year": 2024}
|
|
]
|
|
# Use simple vectors to ensure determinism if we wanted, but random is fine for integration check
|
|
vecs = [np.random.rand(768).astype('float32') for _ in docs]
|
|
meta = [{"category": d["category"], "year": d["year"]} for d in docs]
|
|
v_ids = [f"doc_{d['id']}" for d in docs]
|
|
|
|
# Filter: Category=Tech AND Year=2024
|
|
filt = MetadataFilter().eq("category", "Tech").eq("year", 2024)
|
|
|
|
query = np.random.rand(768).astype('float32')
|
|
results = search.search(query, vecs, meta, v_ids, filter=filt, k=10)
|
|
|
|
# Should only find doc_0
|
|
self.assertEqual(len(results), 1)
|
|
self.assertEqual(results[0]['id'], "doc_0")
|
|
print("Hybrid Search: OK")
|
|
|
|
def test_07_ranking(self):
|
|
"""Test Search Ranker"""
|
|
print("\nTesting Search Ranker...")
|
|
ranker = SearchRanker(strategy="reciprocal_rank_fusion")
|
|
res1 = [{"id": "doc_1", "score": 0.9}, {"id": "doc_2", "score": 0.8}]
|
|
res2 = [{"id": "doc_2", "score": 0.85}, {"id": "doc_3", "score": 0.7}]
|
|
|
|
combined = ranker.rank([res1, res2])
|
|
self.assertTrue(len(combined) > 0)
|
|
|
|
# doc_2 should be high up as it appears in both
|
|
ids = [r['id'] for r in combined]
|
|
self.assertIn("doc_2", ids)
|
|
print("Search Ranker: OK")
|
|
|
|
def test_08_namespaces(self):
|
|
"""Test Namespace Manager"""
|
|
print("\nTesting Namespace Manager...")
|
|
manager = NamespaceManager()
|
|
ns_a = manager.create_namespace("ns_a", "Namespace A")
|
|
|
|
manager.add_vector_to_namespace("doc_1", "ns_a")
|
|
vecs_a = manager.get_namespace_vectors("ns_a")
|
|
|
|
self.assertIn("doc_1", vecs_a)
|
|
|
|
# Access control
|
|
ns_a.set_access_control("user1", ["read"])
|
|
self.assertTrue(ns_a.has_permission("user1", "read"))
|
|
self.assertFalse(ns_a.has_permission("user1", "write"))
|
|
print("Namespace Manager: OK")
|
|
|
|
def test_09_reasoning_core(self):
|
|
"""Test Reasoner core functionality"""
|
|
print("\nTesting Reasoner...")
|
|
reasoner = Reasoner()
|
|
|
|
# Test rule parsing and forward chaining
|
|
reasoner.add_rule("IF Person(?x) AND Parent(?x, ?y) THEN Child(?y, ?x)")
|
|
reasoner.add_fact("Person(John)")
|
|
reasoner.add_fact("Parent(John, Jane)")
|
|
|
|
results = reasoner.forward_chain()
|
|
self.assertTrue(any(r.conclusion == "Child(Jane, John)" for r in results))
|
|
|
|
# Test backward chaining
|
|
proof = reasoner.backward_chain("Child(Jane, John)")
|
|
self.assertIsNotNone(proof)
|
|
self.assertEqual(proof.conclusion, "Child(Jane, John)")
|
|
print("Reasoner: OK")
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main(verbosity=2)
|