Skip to content

Commit 76ac880

Browse files
phernandezclaude
andcommitted
feat(api): add GET /knowledge/graph endpoint for full graph visualization
Returns all entities and resolved relations in a flat node/edge format optimized for graph rendering. Replaces the frontend's use of the recent memory endpoint which only returned a subset of relations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent ad3f265 commit 76ac880

3 files changed

Lines changed: 87 additions & 0 deletions

File tree

src/basic_memory/api/v2/routers/knowledge_router.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
ProjectConfigV2ExternalDep,
2121
AppConfigDep,
2222
EntityRepositoryV2ExternalDep,
23+
RelationRepositoryV2ExternalDep,
2324
ProjectExternalIdPathDep,
2425
TaskSchedulerDep,
2526
FileServiceV2ExternalDep,
@@ -31,6 +32,9 @@
3132
EntityResolveRequest,
3233
EntityResolveResponse,
3334
EntityResponseV2,
35+
GraphEdge,
36+
GraphNode,
37+
GraphResponse,
3438
MoveEntityRequestV2,
3539
MoveDirectoryRequestV2,
3640
DeleteDirectoryRequestV2,
@@ -56,6 +60,50 @@ def _schedule_vector_sync_if_enabled(
5660
)
5761

5862

63+
## Graph endpoint
64+
65+
66+
@router.get("/graph", response_model=GraphResponse)
67+
async def get_graph(
68+
project_id: ProjectExternalIdPathDep,
69+
entity_repository: EntityRepositoryV2ExternalDep,
70+
relation_repository: RelationRepositoryV2ExternalDep,
71+
) -> GraphResponse:
72+
"""Return all entities and resolved relations for knowledge graph visualization.
73+
74+
Returns a flat node/edge structure optimized for rendering with graph libraries.
75+
Only includes resolved relations (where to_id is not null).
76+
"""
77+
logger.info("API v2 request: get_graph")
78+
79+
# Fetch all entities for this project
80+
entities = await entity_repository.find_all(use_load_options=False)
81+
nodes = [
82+
GraphNode(
83+
external_id=entity.external_id,
84+
title=entity.title,
85+
note_type=entity.note_type,
86+
file_path=entity.file_path,
87+
)
88+
for entity in entities
89+
]
90+
91+
# Fetch all resolved relations (to_id is not null) with eager-loaded entities
92+
relations = await relation_repository.find_all()
93+
edges = [
94+
GraphEdge(
95+
from_id=relation.from_entity.external_id,
96+
to_id=relation.to_entity.external_id,
97+
relation_type=relation.relation_type,
98+
)
99+
for relation in relations
100+
if relation.to_entity is not None
101+
]
102+
103+
logger.info(f"API v2 response: graph with {len(nodes)} nodes and {len(edges)} edges")
104+
return GraphResponse(nodes=nodes, edges=edges)
105+
106+
59107
## Resolution endpoint
60108

61109

src/basic_memory/schemas/v2/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
ProjectResolveRequest,
1111
ProjectResolveResponse,
1212
)
13+
from basic_memory.schemas.v2.graph import (
14+
GraphEdge,
15+
GraphNode,
16+
GraphResponse,
17+
)
1318
from basic_memory.schemas.v2.resource import (
1419
CreateResourceRequest,
1520
UpdateResourceRequest,
@@ -25,6 +30,9 @@
2530
"DeleteDirectoryRequestV2",
2631
"ProjectResolveRequest",
2732
"ProjectResolveResponse",
33+
"GraphEdge",
34+
"GraphNode",
35+
"GraphResponse",
2836
"CreateResourceRequest",
2937
"UpdateResourceRequest",
3038
"ResourceResponse",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Graph visualization schemas for the knowledge graph endpoint."""
2+
3+
from typing import Optional
4+
5+
from pydantic import BaseModel, Field
6+
7+
8+
class GraphNode(BaseModel):
9+
"""A node in the knowledge graph visualization."""
10+
11+
external_id: str = Field(..., description="Entity external ID (UUID)")
12+
title: str = Field(..., description="Entity title")
13+
note_type: Optional[str] = Field(None, description="Note type (e.g., note, spec, task)")
14+
file_path: str = Field(..., description="Relative file path")
15+
16+
17+
class GraphEdge(BaseModel):
18+
"""An edge in the knowledge graph visualization."""
19+
20+
from_id: str = Field(..., description="External ID of source entity")
21+
to_id: str = Field(..., description="External ID of target entity")
22+
relation_type: str = Field(..., description="Type of relation")
23+
24+
25+
class GraphResponse(BaseModel):
26+
"""Complete knowledge graph for visualization."""
27+
28+
nodes: list[GraphNode] = Field(default_factory=list, description="All entities as nodes")
29+
edges: list[GraphEdge] = Field(
30+
default_factory=list, description="All resolved relations as edges"
31+
)

0 commit comments

Comments
 (0)