graph_visual_with_opensearch.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. """
  2. Knowledge Graph Visualization with OpenSearch + LightRAG WebUI
  3. This script demonstrates two ways to visualize the knowledge graph
  4. stored in OpenSearch:
  5. 1. **WebUI (recommended)**: Opens the LightRAG WebUI in your browser
  6. for interactive graph exploration with search, filtering, and
  7. force-directed layout.
  8. 2. **Standalone HTML**: Fetches graph data from the LightRAG Server API
  9. and generates an interactive HTML file using Pyvis, similar to
  10. graph_visual_with_html.py but reading from OpenSearch instead of
  11. a local .graphml file.
  12. Prerequisites:
  13. 1. LightRAG Server running with OpenSearch storage:
  14. lightrag-server --host 0.0.0.0 --port 9621
  15. 2. Documents already indexed (e.g., via the WebUI or API)
  16. Usage:
  17. # Open WebUI for interactive exploration
  18. python examples/graph_visual_with_opensearch.py
  19. # Generate standalone HTML file
  20. python examples/graph_visual_with_opensearch.py --html
  21. # Custom server URL and output file
  22. python examples/graph_visual_with_opensearch.py --html --server http://localhost:9621 --output my_graph.html
  23. """
  24. import argparse
  25. import os
  26. import sys
  27. import webbrowser
  28. import pipmaster as pm
  29. if not pm.is_installed("requests"):
  30. pm.install("requests")
  31. if not pm.is_installed("pyvis"):
  32. pm.install("pyvis")
  33. import requests
  34. from pyvis.network import Network
  35. def fetch_graph(server_url: str, label: str = "*", max_nodes: int = 300) -> dict:
  36. """Fetch knowledge graph data from LightRAG Server API."""
  37. url = f"{server_url}/graphs"
  38. params = {"label": label, "max_nodes": max_nodes}
  39. resp = requests.get(url, params=params, timeout=30)
  40. resp.raise_for_status()
  41. return resp.json()
  42. def generate_html(graph_data: dict, output_file: str) -> str:
  43. """Generate an interactive HTML visualization from graph data."""
  44. nodes = graph_data.get("nodes", [])
  45. edges = graph_data.get("edges", [])
  46. if not nodes:
  47. print("No nodes found in the graph. Index some documents first.")
  48. sys.exit(1)
  49. print(f"Building visualization: {len(nodes)} nodes, {len(edges)} edges")
  50. net = Network(height="100vh", notebook=False, cdn_resources="in_line")
  51. # Add nodes with colors based on entity type
  52. import hashlib
  53. for node in nodes:
  54. node_id = node.get("id", "")
  55. props = node.get("properties", {})
  56. entity_type = props.get("entity_type", "unknown")
  57. description = props.get("description", "")
  58. # Deterministic color from entity type
  59. color_hash = int(hashlib.md5(entity_type.encode()).hexdigest()[:6], 16)
  60. color = f"#{color_hash:06x}"
  61. net.add_node(
  62. node_id,
  63. label=node_id,
  64. title=f"[{entity_type}] {description[:200]}"
  65. if description
  66. else entity_type,
  67. color=color,
  68. )
  69. # Add edges
  70. for edge in edges:
  71. source = edge.get("source", "")
  72. target = edge.get("target", "")
  73. props = edge.get("properties", {})
  74. rel_type = edge.get("type", "")
  75. description = props.get("description", "")
  76. net.add_edge(
  77. source,
  78. target,
  79. title=f"[{rel_type}] {description[:200]}" if description else rel_type,
  80. label=rel_type,
  81. )
  82. net.save_graph(output_file)
  83. print(f"Graph saved to {output_file}")
  84. return output_file
  85. def main():
  86. parser = argparse.ArgumentParser(
  87. description="Visualize LightRAG knowledge graph from OpenSearch"
  88. )
  89. parser.add_argument(
  90. "--html",
  91. action="store_true",
  92. help="Generate standalone HTML file instead of opening WebUI",
  93. )
  94. parser.add_argument(
  95. "--server",
  96. default="http://localhost:9621",
  97. help="LightRAG Server URL (default: http://localhost:9621)",
  98. )
  99. parser.add_argument(
  100. "--output",
  101. default="knowledge_graph_opensearch.html",
  102. help="Output HTML file (default: knowledge_graph_opensearch.html)",
  103. )
  104. parser.add_argument(
  105. "--label",
  106. default="*",
  107. help="Starting node label, or '*' for all nodes (default: *)",
  108. )
  109. parser.add_argument(
  110. "--max-nodes",
  111. type=int,
  112. default=300,
  113. help="Maximum nodes to fetch (default: 300)",
  114. )
  115. args = parser.parse_args()
  116. # Verify server is running
  117. try:
  118. requests.get(f"{args.server}/health", timeout=5)
  119. except requests.ConnectionError:
  120. print(f"Error: Cannot connect to LightRAG Server at {args.server}")
  121. print("Start the server first: lightrag-server --host 0.0.0.0 --port 9621")
  122. sys.exit(1)
  123. if args.html:
  124. # Generate standalone HTML
  125. graph_data = fetch_graph(args.server, args.label, args.max_nodes)
  126. output = generate_html(graph_data, args.output)
  127. webbrowser.open(f"file://{os.path.abspath(output)}")
  128. else:
  129. # Open WebUI graph explorer
  130. url = f"{args.server}/#/graph"
  131. print(f"Opening LightRAG WebUI graph explorer: {url}")
  132. webbrowser.open(url)
  133. if __name__ == "__main__":
  134. main()