Skip to content

memory.export

memory.export

GEXF export over the memory KGLite research tree.

A thin, read-only projection of the memory graph (entry nodes + has_child / related edges) into a GEXF 1.2 document. It is a PURE function of a KGLite graph handle — it queries via graph.cypher and mutates nothing — so it is fully unit-testable without the MCP server (a test can build a kglite.KnowledgeGraph, add entry nodes/edges, and export directly).

This module is standalone: memory must NEVER import zettelkasten (the dependency is strictly one-way). It shares only the neutral, in-package :mod:memory.gexf builder.

graph_to_gexf

graph_to_gexf(graph: 'kglite.KnowledgeGraph') -> str

Serialize the entry subgraph of a KGLite graph into GEXF 1.2.

Every LIVE entry node becomes a <node> (label = title, attributes type/status); every has_child and related edge whose BOTH endpoints are emitted entries becomes an <edge> (label = the connection type). Discarded/tombstoned entries are filtered out (not emitted as live nodes), and edges touching a non-entry node (e.g. the project root, or a document) or a filtered entry are omitted so every emitted edge references an emitted node.

Parameters:

Name Type Description Default
graph 'kglite.KnowledgeGraph'

a KGLite graph handle exposing .cypher(query) returning rows with attribute access / dict-coercion.

required
Source code in memory/export.py
def graph_to_gexf(graph: "kglite.KnowledgeGraph") -> str:
    """Serialize the entry subgraph of a KGLite ``graph`` into GEXF 1.2.

    Every LIVE ``entry`` node becomes a ``<node>`` (``label`` = title, attributes
    ``type``/``status``); every ``has_child`` and ``related`` edge whose BOTH
    endpoints are emitted entries becomes an ``<edge>`` (``label`` = the
    connection type). Discarded/tombstoned entries are filtered out (not emitted
    as live nodes), and edges touching a non-entry node (e.g. the project root,
    or a document) or a filtered entry are omitted so every emitted edge
    references an emitted node.

    Args:
        graph: a KGLite graph handle exposing ``.cypher(query)`` returning rows
            with attribute access / ``dict``-coercion.
    """
    node_rows = [
        dict(r)
        for r in graph.cypher(
            "MATCH (e:entry) RETURN e.id AS id, e.title AS title, "
            "e.type AS type, e.status AS status"
        )
    ]
    # Drop discarded/tombstoned entries: they are not part of the live graph.
    node_rows = [
        r
        for r in node_rows
        if r.get("id") and str(r.get("status") or "").lower() not in _DISCARDED_STATUSES
    ]
    entry_ids = {str(r["id"]) for r in node_rows}

    nodes = [
        (
            str(r["id"]),
            str(r.get("title") or r["id"]),
            {"type": r.get("type") or "", "status": r.get("status") or ""},
        )
        for r in node_rows
    ]

    edges: list[tuple[str, str, str, object]] = []
    for conn in ("has_child", "related"):
        rows = [
            dict(r)
            for r in graph.cypher(
                f"MATCH (a:entry)-[:{conn}]->(b:entry) "
                "RETURN a.id AS source, b.id AS target"
            )
        ]
        for r in rows:
            src, tgt = str(r.get("source") or ""), str(r.get("target") or "")
            # Only emit edges between two entries we actually emitted as nodes.
            if src in entry_ids and tgt in entry_ids:
                edges.append((src, tgt, conn, None))

    return build_gexf(nodes, edges, node_attributes=_NODE_ATTRIBUTES)