# Knowledge Graph Features

Mnemex now includes comprehensive knowledge graph capabilities inspired by the reference MCP memory server, adapted for temporal memory management.

## Overview

The knowledge graph provides:

1. **Entity Tracking**: Tag memories with named entities
2. **Explicit Relations**: Create directed links between memories
3. **Graph Navigation**: Read the entire graph or access specific nodes
4. **Temporal Scoring**: All graph operations respect memory decay

## Core Concepts

### Memories as Nodes

Each memory is a node in the graph with:
- **Content**: The actual information stored
- **Entities**: Named entities mentioned (e.g., people, projects, concepts)
- **Metadata**: Tags, source, context
- **Temporal Properties**: Score, use_count, last_used
- **Status**: Active, promoted, or archived

### Relations as Edges

Relations connect memories with:
- **Type**: The nature of the relationship (e.g., "references", "similar_to", "follows_from")
- **Direction**: From one memory to another
- **Strength**: Weight of the relationship (0.0-1.0)
- **Metadata**: Additional context about the relation

### Knowledge Graph Structure

```
{
  "memories": [
    {
      "id": "mem-123",
      "content": "Project X deadline is Friday",
      "entities": ["project-x"],
      "tags": ["deadline", "work"],
      "score": 0.82,
      ...
    }
  ],
  "relations": [
    {
      "from": "mem-123",
      "to": "mem-456",
      "type": "references",
      "strength": 0.9
    }
  ],
  "stats": {
    "total_memories": 150,
    "total_relations": 45,
    "avg_score": 0.42
  }
}
```

## New Tools

### read_graph

Get the complete knowledge graph.

**Use Cases:**
- Visualize the entire memory network
- Export memories for analysis
- Understand memory structure
- Feed full context to LLM

**Example:**

```json
{
  "status": "active",        // Filter: "active", "promoted", "archived", "all"
  "include_scores": true,    // Include temporal decay scores
  "limit": 100               // Optional: limit number of memories
}
```

**Response:**

```json
{
  "success": true,
  "memories": [
    {
      "id": "mem-123",
      "content": "...",
      "entities": ["project-x", "john"],
      "tags": ["work"],
      "score": 0.82,
      "use_count": 5,
      "age_days": 2.5
    }
  ],
  "relations": [
    {
      "from": "mem-123",
      "to": "mem-456",
      "type": "references",
      "strength": 0.9
    }
  ],
  "stats": {
    "total_memories": 150,
    "total_relations": 45,
    "avg_score": 0.42,
    "avg_use_count": 3.2,
    "status_filter": "active"
  }
}
```

---

### open_memories

Retrieve specific memories with their relations.

**Use Cases:**
- Get detailed info about specific memories
- Navigate the graph by following relations
- Context assembly for LLM
- Debugging and inspection

**Example:**

```json
{
  "memory_ids": ["mem-123", "mem-456"],  // Single ID or array
  "include_relations": true,              // Include incoming/outgoing relations
  "include_scores": true                  // Include temporal scores
}
```

**Response:**

```json
{
  "success": true,
  "count": 2,
  "memories": [
    {
      "id": "mem-123",
      "content": "...",
      "entities": ["project-x"],
      "tags": ["work"],
      "score": 0.82,
      "relations": {
        "outgoing": [
          {
            "to": "mem-456",
            "type": "references",
            "strength": 0.9
          }
        ],
        "incoming": [
          {
            "from": "mem-789",
            "type": "similar_to",
            "strength": 0.85
          }
        ]
      }
    }
  ],
  "not_found": []
}
```

---

### create_relation

Create an explicit directed link between two memories.

**Use Cases:**
- Manual linking of related information
- Building knowledge graphs explicitly
- Documenting dependencies
- Creating semantic networks

**Relation Types:**

Common relation types:
- `references`: One memory mentions/cites another
- `follows_from`: Temporal sequence (this came after that)
- `similar_to`: Semantic similarity
- `contradicts`: Conflicting information
- `elaborates_on`: Provides detail about another memory
- `part_of`: Hierarchical relationship

**Example:**

```json
{
  "from_memory_id": "mem-123",
  "to_memory_id": "mem-456",
  "relation_type": "references",
  "strength": 0.9,
  "metadata": {
    "context": "same project",
    "created_by": "manual"
  }
}
```

**Response:**

```json
{
  "success": true,
  "relation_id": "rel-789",
  "from": "mem-123",
  "to": "mem-456",
  "type": "references",
  "strength": 0.9,
  "message": "Relation created: mem-123 --[references]--> mem-456"
}
```

## Usage Patterns

### 1. Entity-Based Navigation

Tag memories with entities for easier retrieval:

```python
# Save with entities
save_memory({
  "content": "John Smith joined Project X as lead engineer",
  "entities": ["john-smith", "project-x"],
  "tags": ["team", "project"]
})

# Later: Find all memories about john-smith
search_memory({
  "query": "john-smith",  # Searches entities too
  "tags": ["team"]
})

# Or read full graph and filter client-side by entity
read_graph({"status": "active"})
```

### 2. Explicit Knowledge Chains

Build chains of related information:

```python
# Memory 1: Initial decision
save_memory({
  "content": "Decided to use PostgreSQL for analytics",
  "entities": ["postgresql", "analytics-project"]
})
# -> Returns mem-123

# Memory 2: Follow-up
save_memory({
  "content": "Set up PostgreSQL cluster with streaming replication",
  "entities": ["postgresql", "infrastructure"]
})
# -> Returns mem-456

# Link them
create_relation({
  "from_memory_id": "mem-456",
  "to_memory_id": "mem-123",
  "relation_type": "implements_decision"
})

# Later: Navigate the chain
open_memories({
  "memory_ids": ["mem-123"],
  "include_relations": true
})
# See that mem-456 implements this decision
```

### 3. Context Assembly

Build rich context by following graph:

```python
# Start with a memory
memories = open_memories({
  "memory_ids": ["mem-123"],
  "include_relations": true
})

# Get related memories
related_ids = [r["to"] for r in memories["memories"][0]["relations"]["outgoing"]]

# Fetch them
related = open_memories({
  "memory_ids": related_ids,
  "include_relations": false
})

# Assemble full context for LLM
context = memories + related
```

### 4. Graph Visualization

Export graph for visualization:

```python
graph = read_graph({
  "status": "active",
  "include_scores": true
})

# Convert to format for visualization tools:
# - Graphviz: dot format
# - D3.js: nodes/links arrays
# - Neo4j: Cypher import
# - Obsidian Canvas: .canvas format
```

## Automatic vs Manual Relations

### Automatic Relations

The clustering tool can auto-detect relations based on similarity:

```python
# Find similar memories
clusters = cluster_memories({
  "strategy": "similarity",
  "threshold": 0.85
})

# STM can suggest relations:
# High similarity (>0.9) -> "similar_to"
# Moderate (>0.8) -> "related_to"
```

### Manual Relations

Explicit relations you create:

```python
create_relation({
  "from_memory_id": "mem-123",
  "to_memory_id": "mem-456",
  "relation_type": "references"
})
```

Both types coexist. Manual relations have higher fidelity but require effort. Automatic relations provide coverage but may be noisy.

## Integration with Temporal Decay

Graph features respect temporal properties:

### 1. Relations Survive Forgetting

If a memory is forgotten (GC'd), its relations are deleted (CASCADE).

But: if one memory is promoted and another forgotten, the relation is preserved in the promoted memory's metadata.

### 2. Scoring Affects Graph Traversal

When following relations, low-scoring memories are less prominent:

```python
# Open memories with scores
open_memories({
  "memory_ids": [...],
  "include_scores": true
})

# Client can filter by score
memories_above_threshold = [m for m in result if m["score"] > 0.3]
```

### 3. Promotion Preserves Relations

When promoting a memory to Obsidian:
- Relations are recorded in note frontmatter
- Links to other memories (if also promoted) become wiki-links
- Un-promoted relation targets are noted as STM references

## Advanced: Graph Queries

While not yet built-in, you can build graph queries client-side:

```python
graph = read_graph({"status": "active"})

# Find all memories that reference project-x
project_x_memories = [
  m for m in graph["memories"]
  if "project-x" in m["entities"]
]

# Find all 2-hop neighbors of a memory
def get_neighbors(memory_id, graph, hops=2):
    neighbors = set()
    current = {memory_id}

    for _ in range(hops):
        next_hop = set()
        for mid in current:
            rels = [r for r in graph["relations"] if r["from"] == mid]
            next_hop.update(r["to"] for r in rels)
        neighbors.update(next_hop)
        current = next_hop

    return neighbors

# Strongly connected components
# Topological sort
# Path finding
# etc.
```

## Comparison to Reference Memory Server

| Feature | Reference Memory | Mnemex |
|---------|-----------------|------------|
| **Primary Unit** | Entity (person, org) | Memory (time-bound info) |
| **Observations** | Attached to entities | N/A (content is primary) |
| **Relations** | Between entities | Between memories |
| **Temporal** | No decay | Exponential decay + promotion |
| **read_graph** | ✅ | ✅ |
| **search_nodes** | ✅ | ✅ (as search_memory) |
| **open_nodes** | ✅ | ✅ (as open_memories) |
| **create_entities** | ✅ | Via save_memory with entities |
| **create_relations** | ✅ | ✅ |
| **Persistence** | Permanent | Temporal → Optional promotion |

## Best Practices

1. **Use Entities Consistently**: Pick a naming scheme and stick to it
   - Good: `"project-x"`, `"john-smith"`
   - Avoid: `"Project X"`, `"John"`, `"john"`

2. **Relation Types**: Define a small set of relation types
   - Too many types → hard to query
   - Too few → lack of semantics
   - Recommended: 5-10 core types

3. **Bidirectional Relations**: Create both directions if needed
   ```python
   create_relation({"from": "A", "to": "B", "type": "references"})
   create_relation({"from": "B", "to": "A", "type": "referenced_by"})
   ```

4. **Metadata**: Use relation metadata for context
   ```python
   {
     "metadata": {
       "confidence": 0.8,
       "source": "auto-detected",
       "created_by": "clustering"
     }
   }
   ```

5. **Graph Size**: Monitor graph growth
   - Use `read_graph().stats` to track size
   - Run GC regularly to prune low-scoring memories
   - Consider archiving old but important memories

## Future Enhancements

Planned features:

1. **Graph Queries**: Built-in query language for graph traversal
2. **Automatic Relation Detection**: NER + coreference resolution
3. **Relation Types Ontology**: Predefined semantic types
4. **Graph Embeddings**: Node2Vec for memory embeddings based on structure
5. **Community Detection**: Find clusters of related memories
6. **Temporal Graph Analysis**: How relationships change over time
