Add knowledge graph manipulation endpoints

Added three new REST API endpoints for direct knowledge graph manipulation:

- POST /graph/entity/create: Create new entities in the knowledge graph
- POST /graph/relation/create: Create relationships between entities
- POST /graph/entities/merge: Merge duplicate/misspelled entities while preserving relationships

The merge endpoint is particularly useful for consolidating entities discovered after document processing, fixing spelling errors, and cleaning up the knowledge graph. All relationships from source entities are transferred to the target entity, with intelligent handling of duplicate relationships.

Updated API documentation in lightrag/api/README.md with usage examples for all three endpoints.
This commit is contained in:
NeelM0906 2025-10-08 15:59:47 -04:00
parent b9c37bd937
commit 9f44e89de7
2 changed files with 244 additions and 0 deletions

View file

@ -543,6 +543,69 @@ You can test the API endpoints using the provided curl commands or through the S
4. Query the system using the query endpoints
5. Trigger document scan if new files are put into the inputs directory
### Graph Manipulation Endpoints
LightRAG provides REST API endpoints for direct knowledge graph manipulation:
#### Create Entity
Create a new entity in the knowledge graph:
```bash
curl -X POST "http://localhost:9621/graph/entity/create" \
-H "Content-Type: application/json" \
-d '{
"entity_name": "Tesla",
"entity_data": {
"description": "Electric vehicle manufacturer",
"entity_type": "ORGANIZATION"
}
}'
```
#### Create Relationship
Create a new relationship between two existing entities:
```bash
curl -X POST "http://localhost:9621/graph/relation/create" \
-H "Content-Type: application/json" \
-d '{
"source_entity": "Elon Musk",
"target_entity": "Tesla",
"relation_data": {
"description": "Elon Musk is the CEO of Tesla",
"keywords": "CEO, founder",
"weight": 1.0
}
}'
```
#### Merge Entities
Consolidate duplicate or misspelled entities while preserving all relationships:
```bash
curl -X POST "http://localhost:9621/graph/entities/merge" \
-H "Content-Type: application/json" \
-d '{
"entities_to_change": ["Elon Msk", "Ellon Musk"],
"entity_to_change_into": "Elon Musk"
}'
```
**What the merge operation does:**
- Deletes the specified source entities
- Transfers all relationships from source entities to the target entity
- Intelligently merges duplicate relationships
- Updates vector embeddings for accurate retrieval
- Preserves the entire graph structure
This is particularly useful for:
- Fixing spelling errors in entity names
- Consolidating duplicate entities discovered after document processing
- Cleaning up the knowledge graph for better query performance
## Asynchronous Document Indexing with Progress Tracking
LightRAG implements asynchronous document indexing to enable frontend monitoring and querying of document processing progress. Upon uploading files or inserting text through designated endpoints, a unique Track ID is returned to facilitate real-time progress monitoring.

View file

@ -25,6 +25,22 @@ class RelationUpdateRequest(BaseModel):
updated_data: Dict[str, Any]
class EntityMergeRequest(BaseModel):
entities_to_change: list[str]
entity_to_change_into: str
class EntityCreateRequest(BaseModel):
entity_name: str
entity_data: Dict[str, Any]
class RelationCreateRequest(BaseModel):
source_entity: str
target_entity: str
relation_data: Dict[str, Any]
def create_graph_routes(rag, api_key: Optional[str] = None):
combined_auth = get_combined_auth_dependency(api_key)
@ -225,4 +241,169 @@ def create_graph_routes(rag, api_key: Optional[str] = None):
status_code=500, detail=f"Error updating relation: {str(e)}"
)
@router.post("/graph/entity/create", dependencies=[Depends(combined_auth)])
async def create_entity(request: EntityCreateRequest):
"""
Create a new entity in the knowledge graph
Args:
request (EntityCreateRequest): Request containing:
- entity_name: Name of the entity
- entity_data: Dictionary of entity properties (e.g., description, entity_type)
Returns:
Dict: Created entity information
Example:
{
"entity_name": "Tesla",
"entity_data": {
"description": "Electric vehicle manufacturer",
"entity_type": "ORGANIZATION"
}
}
"""
try:
# Check if entity already exists
exists = await rag.chunk_entity_relation_graph.has_node(request.entity_name)
if exists:
raise ValueError(f"Entity '{request.entity_name}' already exists")
# Prepare entity data
entity_data = request.entity_data.copy()
entity_data["entity_id"] = request.entity_name
# Create the entity
await rag.chunk_entity_relation_graph.upsert_node(
request.entity_name, entity_data
)
return {
"status": "success",
"message": f"Entity '{request.entity_name}' created successfully",
"data": entity_data,
}
except ValueError as ve:
logger.error(f"Validation error creating entity '{request.entity_name}': {str(ve)}")
raise HTTPException(status_code=400, detail=str(ve))
except Exception as e:
logger.error(f"Error creating entity '{request.entity_name}': {str(e)}")
logger.error(traceback.format_exc())
raise HTTPException(
status_code=500, detail=f"Error creating entity: {str(e)}"
)
@router.post("/graph/relation/create", dependencies=[Depends(combined_auth)])
async def create_relation(request: RelationCreateRequest):
"""
Create a new relationship between two entities in the knowledge graph
Args:
request (RelationCreateRequest): Request containing:
- source_entity: Source entity name
- target_entity: Target entity name
- relation_data: Dictionary of relation properties (e.g., description, keywords, weight)
Returns:
Dict: Created relation information
Example:
{
"source_entity": "Elon Musk",
"target_entity": "Tesla",
"relation_data": {
"description": "Elon Musk is the CEO of Tesla",
"keywords": "CEO, founder",
"weight": 1.0
}
}
"""
try:
# Check if both entities exist
source_exists = await rag.chunk_entity_relation_graph.has_node(
request.source_entity
)
target_exists = await rag.chunk_entity_relation_graph.has_node(
request.target_entity
)
if not source_exists:
raise ValueError(f"Source entity '{request.source_entity}' does not exist")
if not target_exists:
raise ValueError(f"Target entity '{request.target_entity}' does not exist")
# Create the relationship
await rag.chunk_entity_relation_graph.upsert_edge(
request.source_entity, request.target_entity, request.relation_data
)
return {
"status": "success",
"message": f"Relation created successfully between '{request.source_entity}' and '{request.target_entity}'",
"data": {
"source": request.source_entity,
"target": request.target_entity,
**request.relation_data,
},
}
except ValueError as ve:
logger.error(
f"Validation error creating relation between '{request.source_entity}' and '{request.target_entity}': {str(ve)}"
)
raise HTTPException(status_code=400, detail=str(ve))
except Exception as e:
logger.error(
f"Error creating relation between '{request.source_entity}' and '{request.target_entity}': {str(e)}"
)
logger.error(traceback.format_exc())
raise HTTPException(
status_code=500, detail=f"Error creating relation: {str(e)}"
)
@router.post("/graph/entities/merge", dependencies=[Depends(combined_auth)])
async def merge_entities(request: EntityMergeRequest):
"""
Merge multiple entities into a single entity, preserving all relationships.
This endpoint is useful for consolidating duplicate or misspelled entities.
All relationships from the source entities will be transferred to the target entity.
Args:
request (EntityMergeRequest): Request containing:
- entities_to_change: List of entity names to be removed
- entity_to_change_into: Name of the target entity to merge into
Returns:
Dict: Result of the merge operation with merged entity information
Example:
{
"entities_to_change": ["Elon Msk", "Ellon Musk"],
"entity_to_change_into": "Elon Musk"
}
"""
try:
result = await rag.amerge_entities(
source_entities=request.entities_to_change,
target_entity=request.entity_to_change_into,
)
return {
"status": "success",
"message": f"Successfully merged {len(request.entities_to_change)} entities into '{request.entity_to_change_into}'",
"data": result,
}
except ValueError as ve:
logger.error(
f"Validation error merging entities {request.entities_to_change} into '{request.entity_to_change_into}': {str(ve)}"
)
raise HTTPException(status_code=400, detail=str(ve))
except Exception as e:
logger.error(
f"Error merging entities {request.entities_to_change} into '{request.entity_to_change_into}': {str(e)}"
)
logger.error(traceback.format_exc())
raise HTTPException(
status_code=500, detail=f"Error merging entities: {str(e)}"
)
return router