diff --git a/CHANGELOG.md b/CHANGELOG.md index 52740bf9..5f1ed4bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.2.0] - 2026-01-10 + ### Added - **Amazon Neptune Support**: - Added `AmazonNeptuneStore` providing Amazon Neptune graph database integration via Bolt protocol and OpenCypher. diff --git a/README.md b/README.md index 687d7611..de1766d8 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ *The missing fabric between raw data and AI engineering. A comprehensive open-source framework for building semantic layers and knowledge engineering systems that transform unstructured data into AI-ready knowledge — powering Knowledge Graph-Powered RAG (GraphRAG), AI Agents, Multi-Agent Systems, and AI applications with structured semantic knowledge.* -**100% Open Source** • **MIT Licensed** • **Latest Version: 0.1.1** • **Production Ready** • **Community Driven** +**100% Open Source** • **MIT Licensed** • **Latest Version: 0.2.0** • **Production Ready** • **Community Driven** [**Discord**](https://discord.gg/pMHguUzG) diff --git a/RELEASE.md b/RELEASE.md index 5fefedea..65ac7851 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -26,10 +26,10 @@ Before releasing, ensure: The project uses GitHub Actions for automated releases to PyPI. -1. **Tag the commit**: Create a new git tag for the version (e.g., `v0.1.1`). +1. **Tag the commit**: Create a new git tag for the version (e.g., `v0.2.0`). ```bash - git tag -a v0.1.1 -m "Release v0.1.1" - git push origin v0.1.1 + git tag -a v0.2.0 -m "Release v0.2.0" + git push origin v0.2.0 ``` 2. **GitHub Action**: The `Release` workflow will automatically trigger, build the package, create a GitHub Release, and publish to PyPI using Trusted Publishing. diff --git a/SECURITY.md b/SECURITY.md index e253e4de..51b053f7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,6 +6,7 @@ We actively support the following versions of Semantica with security updates: | Version | Supported | | ------- | ------------------ | +| 0.2.0 | :white_check_mark: | | 0.1.1 | :white_check_mark: | | 0.1.0 | :white_check_mark: | | < 0.1.0 | :x: | diff --git a/docs/citation.md b/docs/citation.md index 4a7485ee..5517afc6 100644 --- a/docs/citation.md +++ b/docs/citation.md @@ -12,22 +12,22 @@ How to cite Semantica in academic papers and research. author = {Hawksight AI}, year = {2026}, url = {https://github.com/Hawksight-AI/semantica}, - version = {0.1.1}, + version = {0.2.0}, doi = {10.5281/zenodo.XXXXXXX} } ``` ### APA -Hawksight AI. (2026). *Semantica: An Open Source Framework for Semantic Layers and Knowledge Engineering* (Version 0.1.1) [Computer software]. https://github.com/Hawksight-AI/semantica +Hawksight AI. (2026). *Semantica: An Open Source Framework for Semantic Layers and Knowledge Engineering* (Version 0.2.0) [Computer software]. https://github.com/Hawksight-AI/semantica ### MLA -Hawksight AI. *Semantica: An Open Source Framework for Semantic Layers and Knowledge Engineering*. Version 0.1.1, GitHub, 2026, https://github.com/Hawksight-AI/semantica. +Hawksight AI. *Semantica: An Open Source Framework for Semantic Layers and Knowledge Engineering*. Version 0.2.0, GitHub, 2026, https://github.com/Hawksight-AI/semantica. ### Chicago -Hawksight AI. *Semantica: An Open Source Framework for Semantic Layers and Knowledge Engineering*. Version 0.1.1. GitHub, 2026. https://github.com/Hawksight-AI/semantica. +Hawksight AI. *Semantica: An Open Source Framework for Semantic Layers and Knowledge Engineering*. Version 0.2.0. GitHub, 2026. https://github.com/Hawksight-AI/semantica. ### IEEE -Hawksight AI, "Semantica: An Open Source Framework for Semantic Layers and Knowledge Engineering," Version 0.1.1, GitHub, 2026. [Online]. Available: https://github.com/Hawksight-AI/semantica +Hawksight AI, "Semantica: An Open Source Framework for Semantic Layers and Knowledge Engineering," Version 0.2.0, GitHub, 2026. [Online]. Available: https://github.com/Hawksight-AI/semantica --- diff --git a/pyproject.toml b/pyproject.toml index 6af53021..d53a2a1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "semantica" -version = "0.1.1" +version = "0.2.0" description = "🧠 Semantica - An Open Source Framework for building Semantic Layers and Knowledge Engineering " readme = "README.md" license = {text = "MIT"} diff --git a/semantica/__init__.py b/semantica/__init__.py index 7edb0b7b..774b9b13 100644 --- a/semantica/__init__.py +++ b/semantica/__init__.py @@ -10,7 +10,7 @@ Main exports: - Config: Configuration management """ -__version__ = "0.1.1" +__version__ = "0.2.0" __author__ = "Semantica Contributors" __license__ = "MIT" diff --git a/semantica/semantic_extract/relation_extractor.py b/semantica/semantic_extract/relation_extractor.py index aa4752e2..b4245fce 100644 --- a/semantica/semantic_extract/relation_extractor.py +++ b/semantica/semantic_extract/relation_extractor.py @@ -453,7 +453,6 @@ class RelationExtractor: def _extract_last_resort_relations(self, text: str, entities: List[Entity]) -> List[Relation]: """Last resort relation extraction based on simple adjacency.""" - print(f"DEBUG: Last Resort Relations - {len(entities)} entities") relations = [] # Connect adjacent entities for i in range(len(entities) - 1): @@ -482,7 +481,6 @@ class RelationExtractor: metadata={"extraction_method": "last_resort_adjacency"} ) relations.append(rel) - print(f"DEBUG: Created relation {rel.subject.text} -> {rel.object.text}") return relations def _extract_with_patterns( diff --git a/tests/test_model_selection.py b/tests/test_model_selection.py index 02fea055..0f8484e0 100644 --- a/tests/test_model_selection.py +++ b/tests/test_model_selection.py @@ -32,7 +32,8 @@ class TestModelSelection(unittest.TestCase): def test_generator_switching(self): print("\nTesting EmbeddingGenerator Switching...") - generator = EmbeddingGenerator() + # Initialize with explicit method to ensure consistent starting state for test + generator = EmbeddingGenerator(text={"method": "sentence_transformers"}) # Default check self.assertEqual(generator.get_text_method(), "sentence_transformers") diff --git a/tests/test_semantic_extract_batch_provenance.py b/tests/test_semantic_extract_batch_provenance.py index 44518939..558a056d 100644 --- a/tests/test_semantic_extract_batch_provenance.py +++ b/tests/test_semantic_extract_batch_provenance.py @@ -92,8 +92,8 @@ class TestSemanticExtractBatch(unittest.TestCase): self.assertEqual(len(results), 1) self.assertEqual(results[0]["batch_index"], 0) self.assertEqual(results[0]["document_id"], "doc_1") - self.assertEqual(results[0]["semantic_roles"][0]["batch_index"], 0) - self.assertEqual(results[0]["semantic_roles"][0]["document_id"], "doc_1") + self.assertEqual(results[0]["semantic_roles"][0]["metadata"]["batch_index"], 0) + self.assertEqual(results[0]["semantic_roles"][0]["metadata"]["document_id"], "doc_1") def test_coreference_resolver_batch(self): print("Running test_coreference_resolver_batch") diff --git a/tests/triplet_store/test_triplet_store.py b/tests/triplet_store/test_triplet_store.py index 07381800..d6e679ee 100644 --- a/tests/triplet_store/test_triplet_store.py +++ b/tests/triplet_store/test_triplet_store.py @@ -1,7 +1,7 @@ import unittest from unittest.mock import MagicMock, patch -from semantica.triplet_store.triplet_manager import TripletManager, TripletStore -from semantica.triplet_store.query_engine import QueryEngine, QueryResult +from semantica.triplet_store.triplet_store import TripletStore +from semantica.triplet_store.query_engine import QueryEngine from semantica.semantic_extract.triplet_extractor import Triplet class TestTripletStore(unittest.TestCase): @@ -10,125 +10,91 @@ class TestTripletStore(unittest.TestCase): self.mock_logger = MagicMock() self.mock_tracker = MagicMock() - self.logger_patcher = patch('semantica.triplet_store.triplet_manager.get_logger', return_value=self.mock_logger) - self.tracker_patcher = patch('semantica.triplet_store.triplet_manager.get_progress_tracker', return_value=self.mock_tracker) - self.logger_patcher_qe = patch('semantica.triplet_store.query_engine.get_logger', return_value=self.mock_logger) - self.tracker_patcher_qe = patch('semantica.triplet_store.query_engine.get_progress_tracker', return_value=self.mock_tracker) + self.logger_patcher = patch('semantica.triplet_store.triplet_store.get_logger', return_value=self.mock_logger) + self.tracker_patcher = patch('semantica.triplet_store.triplet_store.get_progress_tracker', return_value=self.mock_tracker) self.logger_patcher.start() self.tracker_patcher.start() - self.logger_patcher_qe.start() - self.tracker_patcher_qe.start() def tearDown(self): self.logger_patcher.stop() self.tracker_patcher.stop() - self.logger_patcher_qe.stop() - self.tracker_patcher_qe.stop() - def test_triplet_manager_init(self): - manager = TripletManager(default_store="main") - self.assertEqual(manager.default_store_id, "main") - self.assertEqual(manager.stores, {}) - - def test_register_store(self): - manager = TripletManager() - store = manager.register_store("main", "blazegraph", "http://localhost:9999") - self.assertIsInstance(store, TripletStore) - self.assertEqual(store.store_id, "main") - self.assertEqual(store.store_type, "blazegraph") + @patch('semantica.triplet_store.blazegraph_store.BlazegraphStore') + def test_triplet_store_init(self, mock_blazegraph_store): + store = TripletStore(backend="blazegraph", endpoint="http://localhost:9999") + self.assertEqual(store.backend_type, "blazegraph") self.assertEqual(store.endpoint, "http://localhost:9999") - self.assertIn("main", manager.stores) + mock_blazegraph_store.assert_called_once() - @patch('semantica.triplet_store.triplet_manager.TripletManager._get_store_backend') - def test_add_triplet(self, mock_get_store_backend): - manager = TripletManager() - manager.register_store("main", "blazegraph", "http://localhost:9999") - - mock_store = MagicMock() - mock_get_store_backend.return_value = mock_store - mock_store.add_triplet.return_value = {"status": "success"} + @patch('semantica.triplet_store.blazegraph_store.BlazegraphStore') + def test_add_triplet(self, mock_blazegraph_store): + # Setup mock backend + mock_backend_instance = MagicMock() + mock_blazegraph_store.return_value = mock_backend_instance + mock_backend_instance.add_triplet.return_value = {"status": "success"} + store = TripletStore(backend="blazegraph") triplet = Triplet(subject="s", predicate="p", object="o") - result = manager.add_triplet(triplet, store_id="main") - self.assertTrue(result["success"]) - self.assertEqual(result["store_id"], "main") - mock_store.add_triplet.assert_called_once_with(triplet) + result = store.add_triplet(triplet) + + self.assertEqual(result, {"status": "success"}) + mock_backend_instance.add_triplet.assert_called_once_with(triplet) - @patch('semantica.triplet_store.triplet_manager.TripletManager._get_store_backend') - def test_add_triplets(self, mock_get_store_backend): - manager = TripletManager() - manager.register_store("main", "blazegraph", "http://localhost:9999") + @patch('semantica.triplet_store.blazegraph_store.BlazegraphStore') + def test_add_triplets(self, mock_blazegraph_store): + # Setup mock backend and bulk loader + mock_backend_instance = MagicMock() + mock_blazegraph_store.return_value = mock_backend_instance - mock_store = MagicMock() - mock_get_store_backend.return_value = mock_store - mock_store.add_triplets.return_value = {"status": "success"} + store = TripletStore(backend="blazegraph") + + # Mock bulk loader + mock_loader = MagicMock() + store.bulk_loader = mock_loader + mock_progress = MagicMock() + mock_progress.metadata = {"success": True} + mock_progress.total_triplets = 2 + mock_progress.loaded_triplets = 2 + mock_progress.failed_triplets = 0 + mock_progress.total_batches = 1 + mock_loader.load_triplets.return_value = mock_progress triplets = [ Triplet(subject="s1", predicate="p1", object="o1"), Triplet(subject="s2", predicate="p2", object="o2") ] - result = manager.add_triplets(triplets, store_id="main", batch_size=2) + result = store.add_triplets(triplets, batch_size=2) self.assertTrue(result["success"]) - self.assertEqual(result["store_id"], "main") - mock_store.add_triplets.assert_called() + self.assertEqual(result["total"], 2) + mock_loader.load_triplets.assert_called_once() - @patch('semantica.triplet_store.triplet_manager.TripletManager._get_store_backend') - def test_get_triplets(self, mock_get_store_backend): - manager = TripletManager() - manager.register_store("main", "blazegraph", "http://localhost:9999") - - mock_store = MagicMock() - mock_get_store_backend.return_value = mock_store + @patch('semantica.triplet_store.blazegraph_store.BlazegraphStore') + def test_get_triplets(self, mock_blazegraph_store): + mock_backend_instance = MagicMock() + mock_blazegraph_store.return_value = mock_backend_instance expected_triplets = [Triplet(subject="s", predicate="p", object="o")] - mock_store.get_triplets.return_value = expected_triplets + mock_backend_instance.get_triplets.return_value = expected_triplets - result = manager.get_triplets(subject="s", store_id="main") + store = TripletStore(backend="blazegraph") + result = store.get_triplets(subject="s") self.assertEqual(result, expected_triplets) - mock_store.get_triplets.assert_called_once_with("s", None, None) + mock_backend_instance.get_triplets.assert_called_once_with(subject="s", predicate=None, object=None) - @patch('semantica.triplet_store.triplet_manager.TripletManager._get_store_backend') - def test_delete_triplet(self, mock_get_store_backend): - manager = TripletManager() - manager.register_store("main", "blazegraph", "http://localhost:9999") - - mock_store = MagicMock() - mock_get_store_backend.return_value = mock_store - mock_store.delete_triplet.return_value = {"status": "deleted"} + @patch('semantica.triplet_store.blazegraph_store.BlazegraphStore') + def test_delete_triplet(self, mock_blazegraph_store): + mock_backend_instance = MagicMock() + mock_blazegraph_store.return_value = mock_backend_instance + mock_backend_instance.delete_triplet.return_value = {"success": True} + store = TripletStore(backend="blazegraph") triplet = Triplet(subject="s", predicate="p", object="o") - result = manager.delete_triplet(triplet, store_id="main") + + result = store.delete_triplet(triplet) self.assertTrue(result["success"]) - mock_store.delete_triplet.assert_called_once_with(triplet) - - @patch('semantica.triplet_store.triplet_manager.TripletManager._get_store_backend') - def test_update_triplet(self, mock_get_store_backend): - manager = TripletManager() - manager.register_store("main", "blazegraph", "http://localhost:9999") - - mock_store = MagicMock() - mock_get_store_backend.return_value = mock_store - mock_store.delete_triplet.return_value = {"status": "deleted"} - mock_store.add_triplet.return_value = {"status": "added"} - - old_triplet = Triplet(subject="s", predicate="p", object="o_old") - new_triplet = Triplet(subject="s", predicate="p", object="o_new") - - result = manager.update_triplet(old_triplet, new_triplet, store_id="main") - - self.assertTrue(result["success"]) - mock_store.delete_triplet.assert_called_once_with(old_triplet) - mock_store.add_triplet.assert_called_once_with(new_triplet) - - def test_query_engine_init(self): - engine = QueryEngine(enable_caching=True) - self.assertTrue(engine.enable_caching) - self.assertEqual(engine.query_cache, {}) - -if __name__ == '__main__': - unittest.main() + mock_backend_instance.delete_triplet.assert_called_once_with(triplet)