""" Interactive HTML graph visualization of memory system. Uses Cytoscape.js to create a performant, interactive network graph that can be explored in the browser. Shows all memory units and their links with weights. """ import psycopg2 from dotenv import load_dotenv import os import json load_dotenv() def create_interactive_graph(): """Create an interactive HTML graph visualization using Cytoscape.js.""" # Connect to database conn = psycopg2.connect(os.getenv('DATABASE_URL')) cursor = conn.cursor() # Get all memory units (no agent_id filter) cursor.execute(""" SELECT id, text, event_date, context FROM memory_units ORDER BY event_date """) units = cursor.fetchall() # Get all links with weights (no agent_id filter) cursor.execute(""" SELECT ml.from_unit_id, ml.to_unit_id, ml.link_type, ml.weight, e.canonical_name as entity_name FROM memory_links ml LEFT JOIN entities e ON ml.entity_id = e.id ORDER BY ml.link_type, ml.weight DESC """) links = cursor.fetchall() # Get entity information (no agent_id filter) cursor.execute(""" SELECT ue.unit_id, e.canonical_name, e.entity_type FROM unit_entities ue JOIN entities e ON ue.entity_id = e.id ORDER BY ue.unit_id """) unit_entities = cursor.fetchall() cursor.close() conn.close() # Build entity mapping entity_map = {} for unit_id, entity_name, entity_type in unit_entities: if unit_id not in entity_map: entity_map[unit_id] = [] entity_map[unit_id].append(f"{entity_name} ({entity_type})") # Build Cytoscape.js graph data cy_nodes = [] cy_edges = [] # Add nodes for unit_id, text, event_date, context in units: entities = entity_map.get(unit_id, []) entity_count = len(entities) # Color by entity count if entity_count == 0: color = "#e0e0e0" elif entity_count == 1: color = "#90caf9" else: color = "#42a5f5" cy_nodes.append({ "data": { "id": str(unit_id), "label": text[:50] + "..." if len(text) > 50 else text, "text": text, "context": context, "date": str(event_date.date()), "entities": ", ".join(entities) if entities else "None", "color": color } }) # Add edges for from_id, to_id, link_type, weight, entity_name in links: # Set color based on link type if link_type == 'temporal': color = "#00bcd4" line_style = "dashed" elif link_type == 'semantic': color = "#ff69b4" line_style = "solid" elif link_type == 'entity': color = "#ffd700" line_style = "solid" else: color = "#999999" line_style = "solid" cy_edges.append({ "data": { "id": f"{from_id}-{to_id}-{link_type}", "source": str(from_id), "target": str(to_id), "weight": weight, "linkType": link_type, "entityName": entity_name or "", "color": color, "lineStyle": line_style } }) graph_data = {"nodes": cy_nodes, "edges": cy_edges} # Build table rows for table view table_rows = [] for unit_id, text, event_date, context in units: entities = entity_map.get(unit_id, []) entity_str = ", ".join(entities) if entities else "None" table_rows.append(f"""
| ID | Text | Context | Date | Entities |
|---|