From efda638f8abe78a7b5ea55293e29e876bddaeefc Mon Sep 17 00:00:00 2001 From: IshanikaAurelia1106 Date: Thu, 2 Jul 2026 19:49:01 +0530 Subject: [PATCH 1/3] chore(synapse): replace dependency ImportErrors with unified AI export message --- src/logseq_matryca_parser/synapse.py | 39 +++++++++++++++++++--------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/logseq_matryca_parser/synapse.py b/src/logseq_matryca_parser/synapse.py index 72013d2..dacd3a4 100644 --- a/src/logseq_matryca_parser/synapse.py +++ b/src/logseq_matryca_parser/synapse.py @@ -44,6 +44,7 @@ class SynapseMetadata(TypedDict, total=False): line_start: NotRequired[int | None] effective_properties: NotRequired[dict[str, Any]] + Document: type[Any] | None NodeRelationship: Any RelatedNodeInfo: type[Any] | None @@ -237,7 +238,8 @@ def visit_node(self, node: LogseqNode) -> None: text_node = self._text_node_cls( id_=node.uuid, text=node.clean_text, - metadata=build_synapse_metadata(node, source=node.source_path or ""), + metadata=build_synapse_metadata( + node, source=node.source_path or ""), ) if not hasattr(text_node, "relationships") or text_node.relationships is None: text_node.relationships = {} @@ -259,7 +261,8 @@ def visit_node(self, node: LogseqNode) -> None: child_relationships = parent_node.relationships.get( self._node_relationship.CHILD, [] ) - child_relationships.append(self._related_node_info_cls(node_id=node.uuid)) + child_relationships.append( + self._related_node_info_cls(node_id=node.uuid)) parent_node.relationships[self._node_relationship.CHILD] = child_relationships if node.left_id: @@ -289,8 +292,11 @@ class SynapseAdapter: def to_langchain_documents(nodes: list[LogseqNode], source_name: str) -> list[Any]: """Convert AST nodes to LangChain documents using `LangChainVisitor`.""" if Document is None: - raise ImportError("LangChain non rilevato. Installa 'langchain-core' per usare Synapse.") - visitor = LangChainVisitor(source_name=source_name, document_cls=Document) + raise ImportError( + "Missing AI export dependencies. Install with: uv sync --extra ai" + ) + visitor = LangChainVisitor( + source_name=source_name, document_cls=Document) for node in nodes: node.accept(visitor) return visitor.get_documents() @@ -304,7 +310,9 @@ def to_llamaindex_nodes( ) -> list[Any]: """Convert AST nodes to LlamaIndex nodes preserving topology links.""" if TextNode is None or NodeRelationship is None or RelatedNodeInfo is None: - raise ImportError("LlamaIndex non rilevato. Installa 'llama-index' per usare Synapse.") + raise ImportError( + "Missing AI export dependencies. Install with: uv sync --extra ai" + ) flat = _flatten_nodes_for_export(nodes) unique_paths = {node.source_path for node in flat if node.source_path} use_per_node_source = len(unique_paths) > 1 @@ -313,8 +321,10 @@ def to_llamaindex_nodes( def _source_id_for_node(node: LogseqNode) -> str: path_key = node.source_path or "" if path_key not in source_ids_by_path: - title_seed = page_title or (Path(path_key).stem if path_key else "untitled") - source_ids_by_path[path_key] = page_source_node_id(title_seed, path_key or None) + title_seed = page_title or ( + Path(path_key).stem if path_key else "untitled") + source_ids_by_path[path_key] = page_source_node_id( + title_seed, path_key or None) return source_ids_by_path[path_key] resolved_source_id = page_source_id @@ -341,7 +351,9 @@ def to_context_enriched_chunks( ) -> list[Any]: """Flatten ``nodes`` and emit LangChain ``Document``s with breadcrumb-enriched ``page_content``.""" if Document is None: - raise ImportError("LangChain non rilevato. Installa 'langchain-core' per usare Synapse.") + raise ImportError( + "Missing AI export dependencies. Install with: uv sync --extra ai" + ) documents: list[Any] = [] flat = _flatten_nodes_for_export(nodes) for node in flat: @@ -349,10 +361,12 @@ def to_context_enriched_chunks( logger.debug("context chunk skip orphan uuid=%s", node.uuid) continue breadcrumbs, page = _build_breadcrumbs(graph, node) - source_name = Path(node.source_path).name if node.source_path else str(graph.graph_path.name) + source_name = Path(node.source_path).name if node.source_path else str( + graph.graph_path.name) host_page = graph.page_for_node(node) embed_chain = ( - frozenset({host_page.title}) if host_page is not None else frozenset() + frozenset({host_page.title} + ) if host_page is not None else frozenset() ) expanded_content = _expand_macros_and_embeds( node.content, graph, set(), embed_page_chain=embed_chain @@ -376,7 +390,8 @@ def to_context_enriched_chunks( "effective_properties": effective_properties, }, ) - documents.append(Document(page_content=page_content, metadata=metadata)) + documents.append( + Document(page_content=page_content, metadata=metadata)) logger.debug( "context chunk uuid=%s breadcrumbs_len=%s effective_keys=%s", node.uuid, @@ -390,4 +405,4 @@ def load_and_convert(cls, file_path: Path) -> list[Any]: """Parse a file and convert it to LangChain documents.""" parser = LogosParser() nodes = parser.parse_file(file_path) - return cls.to_langchain_documents(nodes, source_name=file_path.name) \ No newline at end of file + return cls.to_langchain_documents(nodes, source_name=file_path.name) From fb365968814ebcac539995c3fb9794bff53a64b9 Mon Sep 17 00:00:00 2001 From: IshanikaAurelia1106 Date: Sat, 4 Jul 2026 17:41:45 +0530 Subject: [PATCH 2/3] test(synapse): update tests for unified AI dependency message --- src/logseq_matryca_parser/synapse.py | 20 ++++++++------ tests/test_synapse.py | 41 ++++++++++++++++++---------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/logseq_matryca_parser/synapse.py b/src/logseq_matryca_parser/synapse.py index dacd3a4..cc32312 100644 --- a/src/logseq_matryca_parser/synapse.py +++ b/src/logseq_matryca_parser/synapse.py @@ -20,6 +20,10 @@ _PATH_JOIN = " > " _REFS_JOIN = ", " +_MISSING_AI_EXPORT_DEPS_MSG = ( + "Missing AI export dependencies. Please install them using: uv sync --extra ai" +) + class SynapseMetadata(TypedDict, total=False): """Vector-store-safe metadata schema for LangChain / LlamaIndex exports.""" @@ -292,9 +296,9 @@ class SynapseAdapter: def to_langchain_documents(nodes: list[LogseqNode], source_name: str) -> list[Any]: """Convert AST nodes to LangChain documents using `LangChainVisitor`.""" if Document is None: - raise ImportError( - "Missing AI export dependencies. Install with: uv sync --extra ai" - ) + + raise ImportError(_MISSING_AI_EXPORT_DEPS_MSG) + visitor = LangChainVisitor( source_name=source_name, document_cls=Document) for node in nodes: @@ -310,9 +314,9 @@ def to_llamaindex_nodes( ) -> list[Any]: """Convert AST nodes to LlamaIndex nodes preserving topology links.""" if TextNode is None or NodeRelationship is None or RelatedNodeInfo is None: - raise ImportError( - "Missing AI export dependencies. Install with: uv sync --extra ai" - ) + + raise ImportError(_MISSING_AI_EXPORT_DEPS_MSG) + flat = _flatten_nodes_for_export(nodes) unique_paths = {node.source_path for node in flat if node.source_path} use_per_node_source = len(unique_paths) > 1 @@ -351,9 +355,7 @@ def to_context_enriched_chunks( ) -> list[Any]: """Flatten ``nodes`` and emit LangChain ``Document``s with breadcrumb-enriched ``page_content``.""" if Document is None: - raise ImportError( - "Missing AI export dependencies. Install with: uv sync --extra ai" - ) + raise ImportError(_MISSING_AI_EXPORT_DEPS_MSG) documents: list[Any] = [] flat = _flatten_nodes_for_export(nodes) for node in flat: diff --git a/tests/test_synapse.py b/tests/test_synapse.py index d54f83b..9920e38 100644 --- a/tests/test_synapse.py +++ b/tests/test_synapse.py @@ -70,13 +70,15 @@ def build_ast() -> list[LogseqNode]: def test_to_langchain_documents_raises_when_dependency_missing() -> None: with patch("logseq_matryca_parser.synapse.Document", None): - with pytest.raises(ImportError, match="LangChain"): - SynapseAdapter.to_langchain_documents(build_ast(), source_name="test.md") + with pytest.raises(ImportError, match="Missing AI export dependencies"): + SynapseAdapter.to_langchain_documents( + build_ast(), source_name="test.md") def test_to_langchain_documents_uses_visitor_and_graph_metadata() -> None: with patch("logseq_matryca_parser.synapse.Document", FakeDocument): - docs = SynapseAdapter.to_langchain_documents(build_ast(), source_name="graph.md") + docs = SynapseAdapter.to_langchain_documents( + build_ast(), source_name="graph.md") assert len(docs) == 2 root_doc = docs[0] @@ -106,7 +108,7 @@ def test_to_llamaindex_nodes_raises_when_dependency_missing() -> None: patch("logseq_matryca_parser.synapse.NodeRelationship", None), patch("logseq_matryca_parser.synapse.RelatedNodeInfo", None), ): - with pytest.raises(ImportError, match="LlamaIndex"): + with pytest.raises(ImportError, match="Missing AI export dependencies"): SynapseAdapter.to_llamaindex_nodes(build_ast()) @@ -173,7 +175,8 @@ def test_to_llamaindex_nodes_assigns_distinct_source_per_page() -> None: ): nodes = SynapseAdapter.to_llamaindex_nodes([root_a, root_b]) - source_ids = {nodes[0].relationships["SOURCE"].node_id, nodes[1].relationships["SOURCE"].node_id} + source_ids = {nodes[0].relationships["SOURCE"].node_id, + nodes[1].relationships["SOURCE"].node_id} assert len(source_ids) == 2 @@ -212,7 +215,8 @@ def test_to_llamaindex_nodes_wires_sibling_next_and_previous() -> None: patch("logseq_matryca_parser.synapse.NodeRelationship", fake_relationship), patch("logseq_matryca_parser.synapse.RelatedNodeInfo", FakeRelatedNodeInfo), ): - nodes = SynapseAdapter.to_llamaindex_nodes([root], page_source_id="page-doc") + nodes = SynapseAdapter.to_llamaindex_nodes( + [root], page_source_id="page-doc") by_id = {node.id_: node for node in nodes} assert by_id["sibling-b"].relationships["PREVIOUS"].node_id == "sibling-a" @@ -221,7 +225,7 @@ def test_to_llamaindex_nodes_wires_sibling_next_and_previous() -> None: def test_to_context_enriched_chunks_raises_when_dependency_missing(tmp_path: Path) -> None: with patch("logseq_matryca_parser.synapse.Document", None): - with pytest.raises(ImportError, match="LangChain"): + with pytest.raises(ImportError, match="Missing AI export dependencies"): graph = LogseqGraph(graph_path=tmp_path, pages={}) SynapseAdapter.to_context_enriched_chunks([], graph) @@ -243,7 +247,8 @@ def test_synapse_context_enriched_chunking(tmp_path: Path) -> None: demo = graph.pages["Demo"] with patch("logseq_matryca_parser.synapse.Document", FakeDocument): - chunks = SynapseAdapter.to_context_enriched_chunks(demo.root_nodes, graph) + chunks = SynapseAdapter.to_context_enriched_chunks( + demo.root_nodes, graph) assert len(chunks) == 2 child_chunk = chunks[1] @@ -277,8 +282,10 @@ def test_synapse_recursive_embed_expansion(tmp_path: Path) -> None: "- Before {{embed ((" + block_id + "))}} after\n", encoding="utf-8", ) - (pages / "SnippetPage.md").write_text("- Line one from snippet\n- Line two from snippet\n", encoding="utf-8") - (pages / "PageEmbedHost.md").write_text("- Start {{embed [[SnippetPage]]}} end\n", encoding="utf-8") + (pages / "SnippetPage.md").write_text( + "- Line one from snippet\n- Line two from snippet\n", encoding="utf-8") + (pages / "PageEmbedHost.md").write_text( + "- Start {{embed [[SnippetPage]]}} end\n", encoding="utf-8") graph = LogseqGraph.load_directory(graph_root) @@ -309,7 +316,8 @@ def test_expand_embed_missing_page_completes_without_hang(tmp_path: Path) -> Non graph_root = tmp_path / "vault" pages = graph_root / "pages" pages.mkdir(parents=True) - (pages / "P.md").write_text("- x {{embed [[NoSuchPage]]}}\n", encoding="utf-8") + (pages / + "P.md").write_text("- x {{embed [[NoSuchPage]]}}\n", encoding="utf-8") graph = LogseqGraph.load_directory(graph_root) text = graph.pages["P"].root_nodes[0].content @@ -342,14 +350,16 @@ def test_expand_cyclic_page_embed_does_not_duplicate_parent_text(tmp_path: Path) graph_root = tmp_path / "vault" pages = graph_root / "pages" pages.mkdir(parents=True) - (pages / "A.md").write_text("- before {{embed [[B]]}} after\n", encoding="utf-8") + (pages / + "A.md").write_text("- before {{embed [[B]]}} after\n", encoding="utf-8") (pages / "B.md").write_text("- inner {{embed [[A]]}}\n", encoding="utf-8") graph = LogseqGraph.load_directory(graph_root) host_page = graph.pages["A"] text = host_page.root_nodes[0].content chain = frozenset({host_page.title}) - expanded = _expand_macros_and_embeds(text, graph, set(), embed_page_chain=chain) + expanded = _expand_macros_and_embeds( + text, graph, set(), embed_page_chain=chain) assert expanded.strip() == "before inner after" @@ -361,7 +371,8 @@ def test_expand_missing_block_embed_completes_without_hang(tmp_path: Path) -> No pages = graph_root / "pages" pages.mkdir(parents=True) missing = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" - (pages / "P.md").write_text(f"- {{{{embed (({missing}))}}}}\n", encoding="utf-8") + (pages / + "P.md").write_text(f"- {{{{embed (({missing}))}}}}\n", encoding="utf-8") graph = LogseqGraph.load_directory(graph_root) text = graph.pages["P"].root_nodes[0].content @@ -411,7 +422,7 @@ def test_includes_core_keys(self): node = LogseqNode(uuid="abc", content="Test", indent_level=0) meta = build_synapse_metadata(node, source="test") for key in ("uuid", "indent_level", "source", "path", "refs", - "task_status", "task_priority"): + "task_status", "task_priority"): assert key in meta def test_property_serialization(self): From 61c29206b35c024e6863068250516a3ceebd6652 Mon Sep 17 00:00:00 2001 From: IshanikaAurelia1106 Date: Sun, 5 Jul 2026 15:24:57 +0530 Subject: [PATCH 3/3] docs: update changelog and sync dependency message tests --- CHANGELOG.md | 2 +- tests/test_synapse.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 808012f..5b1faae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to **logseq-matryca-parser** (The Logos Protocol) are docume The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +### Changed ## [1.6.0] - 2026-07-02 diff --git a/tests/test_synapse.py b/tests/test_synapse.py index 9920e38..89d4847 100644 --- a/tests/test_synapse.py +++ b/tests/test_synapse.py @@ -11,6 +11,7 @@ from logseq_matryca_parser.logos_core import LogseqNode from logseq_matryca_parser.synapse import ( SynapseAdapter, + _MISSING_AI_EXPORT_DEPS_MSG, _strip_markdown_for_embedding, build_synapse_metadata, ) @@ -70,7 +71,7 @@ def build_ast() -> list[LogseqNode]: def test_to_langchain_documents_raises_when_dependency_missing() -> None: with patch("logseq_matryca_parser.synapse.Document", None): - with pytest.raises(ImportError, match="Missing AI export dependencies"): + with pytest.raises(ImportError, match=_MISSING_AI_EXPORT_DEPS_MSG): SynapseAdapter.to_langchain_documents( build_ast(), source_name="test.md") @@ -108,7 +109,7 @@ def test_to_llamaindex_nodes_raises_when_dependency_missing() -> None: patch("logseq_matryca_parser.synapse.NodeRelationship", None), patch("logseq_matryca_parser.synapse.RelatedNodeInfo", None), ): - with pytest.raises(ImportError, match="Missing AI export dependencies"): + with pytest.raises(ImportError, match=_MISSING_AI_EXPORT_DEPS_MSG): SynapseAdapter.to_llamaindex_nodes(build_ast()) @@ -225,7 +226,7 @@ def test_to_llamaindex_nodes_wires_sibling_next_and_previous() -> None: def test_to_context_enriched_chunks_raises_when_dependency_missing(tmp_path: Path) -> None: with patch("logseq_matryca_parser.synapse.Document", None): - with pytest.raises(ImportError, match="Missing AI export dependencies"): + with pytest.raises(ImportError, match=_MISSING_AI_EXPORT_DEPS_MSG): graph = LogseqGraph(graph_path=tmp_path, pages={}) SynapseAdapter.to_context_enriched_chunks([], graph)