Skip to content

Graph Visualization Example

This example demonstrates how to visualize and inspect graph structures using the Semantic Kernel Graph's visualization capabilities. It shows how to export graphs in various formats, create real-time visualizations, and implement interactive graph inspection.

Objective

Learn how to implement graph visualization and inspection in graph-based workflows to: * Export graphs in multiple formats (DOT, JSON, Mermaid) * Create real-time visualizations with execution highlights * Implement interactive graph inspection and debugging * Generate visual representations for documentation and analysis * Monitor graph execution with visual feedback

Prerequisites

Key Components

Concepts and Techniques

  • Graph Visualization: Converting graph structures to visual representations
  • Export Formats: Supporting multiple visualization formats (DOT, JSON, Mermaid)
  • Real-Time Highlights: Visual feedback during graph execution
  • Interactive Inspection: Debugging and analyzing graph structures
  • Execution Overlays: Visual representation of execution flow

Core Classes

  • GraphVisualizationEngine: Core visualization engine
  • GraphRealtimeHighlighter: Real-time execution highlighting
  • GraphInspectionApi: Interactive graph inspection
  • GraphVisualizationOptions: Configuration for visualization

Running the Example

Getting Started

This example demonstrates graph visualization and export capabilities with the Semantic Kernel Graph package. The code snippets below show you how to implement this pattern in your own applications.

Step-by-Step Implementation

1. Basic Graph Visualization

This example demonstrates basic graph export and visualization capabilities.

// Create kernel with mock configuration
var kernel = CreateKernel();

// Create visualization-enabled workflow
var visualizationWorkflow = new GraphExecutor("VisualizationWorkflow", "Basic graph visualization", logger);

// Configure visualization options
var visualizationOptions = new GraphVisualizationOptions
{
    EnableDOTExport = true,
    EnableJSONExport = true,
    EnableMermaidExport = true,
    EnableRealTimeHighlights = true,
    EnableExecutionOverlays = true,
    ExportPath = "./graph-exports"
};

visualizationWorkflow.ConfigureVisualization(visualizationOptions);

// Sample processing nodes
var inputProcessor = new FunctionGraphNode(
    "input-processor",
    "Process input data",
    async (context) =>
    {
        var inputData = context.GetValue<string>("input_data");
        var processedData = $"Processed: {inputData}";

        context.SetValue("processed_data", processedData);
        context.SetValue("processing_step", "input_processed");

        return processedData;
    });

var dataTransformer = new FunctionGraphNode(
    "data-transformer",
    "Transform processed data",
    async (context) =>
    {
        var processedData = context.GetValue<string>("processed_data");
        var transformedData = $"Transformed: {processedData.ToUpper()}";

        context.SetValue("transformed_data", transformedData);
        context.SetValue("processing_step", "data_transformed");

        return transformedData;
    });

var outputGenerator = new FunctionGraphNode(
    "output-generator",
    "Generate final output",
    async (context) =>
    {
        var transformedData = context.GetValue<string>("transformed_data");
        var finalOutput = $"Final Output: {transformedData}";

        context.SetValue("final_output", finalOutput);
        context.SetValue("processing_step", "output_generated");

        return finalOutput;
    });

// Add nodes to workflow
visualizationWorkflow.AddNode(inputProcessor);
visualizationWorkflow.AddNode(dataTransformer);
visualizationWorkflow.AddNode(outputGenerator);

// Set start node
visualizationWorkflow.SetStartNode(inputProcessor.NodeId);

// Export graph in different formats
Console.WriteLine("📊 Exporting graph in different formats...");

// DOT format export
var dotExport = await visualizationWorkflow.ExportToDOTAsync();
Console.WriteLine($"   DOT Export: {dotExport.Length} characters");
File.WriteAllText("./graph-exports/workflow.dot", dotExport);

// JSON format export
var jsonExport = await visualizationWorkflow.ExportToJSONAsync();
Console.WriteLine($"   JSON Export: {jsonExport.Length} characters");
File.WriteAllText("./graph-exports/workflow.json", jsonExport);

// Mermaid format export
var mermaidExport = await visualizationWorkflow.ExportToMermaidAsync();
Console.WriteLine($"   Mermaid Export: {mermaidExport.Length} characters");
File.WriteAllText("./graph-exports/workflow.mmd", mermaidExport);

// Test visualization workflow
var testData = new[]
{
    "Sample data 1",
    "Sample data 2",
    "Sample data 3"
};

foreach (var data in testData)
{
    var arguments = new KernelArguments
    {
        ["input_data"] = data
    };

    Console.WriteLine($"🧪 Testing visualization workflow: {data}");
    var result = await visualizationWorkflow.ExecuteAsync(kernel, arguments);

    var processingStep = result.GetValue<string>("processing_step");
    var finalOutput = result.GetValue<string>("final_output");

    Console.WriteLine($"   Processing Step: {processingStep}");
    Console.WriteLine($"   Final Output: {finalOutput}");
    Console.WriteLine();
}

2. Real-Time Execution Visualization

Demonstrates real-time visualization with execution highlights and overlays.

// Create real-time visualization workflow
var realTimeVisualizationWorkflow = new GraphExecutor("RealTimeVisualizationWorkflow", "Real-time execution visualization", logger);

// Configure real-time visualization
var realTimeVisualizationOptions = new GraphVisualizationOptions
{
    EnableRealTimeHighlights = true,
    EnableExecutionOverlays = true,
    EnableLiveUpdates = true,
    UpdateInterval = TimeSpan.FromMilliseconds(500),
    EnableExecutionTracking = true,
    EnableNodeStateHighlighting = true,
    ExportPath = "./real-time-exports"
};

realTimeVisualizationWorkflow.ConfigureVisualization(realTimeVisualizationOptions);

// Real-time data processor
var realTimeProcessor = new FunctionGraphNode(
    "real-time-processor",
    "Process data with real-time visualization",
    async (context) =>
    {
        var inputData = context.GetValue<string>("input_data");
        var iteration = context.GetValue<int>("iteration", 0);

        // Simulate processing with delays
        await Task.Delay(Random.Shared.Next(100, 300));

        var processedData = $"Real-time processed: {inputData} (iteration {iteration + 1})";

        context.SetValue("processed_data", processedData);
        context.SetValue("iteration", iteration + 1);
        context.SetValue("processing_timestamp", DateTime.UtcNow);
        context.SetValue("node_state", "completed");

        return processedData;
    });

// Real-time visualizer
var realTimeVisualizer = new FunctionGraphNode(
    "real-time-visualizer",
    "Update real-time visualization",
    async (context) =>
    {
        var processedData = context.GetValue<string>("processed_data");
        var iteration = context.GetValue<int>("iteration");
        var timestamp = context.GetValue<DateTime>("processing_timestamp");
        var nodeState = context.GetValue<string>("node_state");

        // Update real-time visualization
        var visualizationUpdate = new Dictionary<string, object>
        {
            ["current_iteration"] = iteration,
            ["last_processed_data"] = processedData,
            ["last_timestamp"] = timestamp,
            ["node_states"] = new Dictionary<string, string>
            {
                ["real-time-processor"] = nodeState,
                ["real-time-visualizer"] = "active"
            },
            ["execution_progress"] = (double)iteration / 10.0, // Assuming 10 iterations
            ["visualization_updated"] = true
        };

        context.SetValue("visualization_update", visualizationUpdate);

        // Export current state
        var currentDOT = await realTimeVisualizationWorkflow.ExportToDOTAsync();
        var currentJSON = await realTimeVisualizationWorkflow.ExportToJSONAsync();

        File.WriteAllText($"./real-time-exports/iteration_{iteration}.dot", currentDOT);
        File.WriteAllText($"./real-time-exports/iteration_{iteration}.json", currentJSON);

        return $"Visualization updated for iteration {iteration}";
    });

// Add nodes to real-time workflow
realTimeVisualizationWorkflow.AddNode(realTimeProcessor);
realTimeVisualizationWorkflow.AddNode(realTimeVisualizer);

// Set start node
realTimeVisualizationWorkflow.SetStartNode(realTimeProcessor.NodeId);

// Test real-time visualization
Console.WriteLine("🎬 Starting real-time visualization...");
Console.WriteLine("   Visualization will update every 500ms");
Console.WriteLine("   Exports will be saved to ./real-time-exports/");

var realTimeArguments = new KernelArguments
{
    ["input_data"] = "Real-time test data",
    ["iteration"] = 0
};

// Run real-time visualization for several iterations
for (int i = 0; i < 5; i++)
{
    var result = await realTimeVisualizationWorkflow.ExecuteAsync(kernel, realTimeArguments);

    var visualizationUpdate = result.GetValue<Dictionary<string, object>>("visualization_update");
    var iteration = result.GetValue<int>("iteration");

    if (visualizationUpdate != null)
    {
        Console.WriteLine($"   Iteration {iteration}: " +
                         $"Data: {visualizationUpdate["last_processed_data"]}, " +
                         $"Progress: {visualizationUpdate["execution_progress"]:P0}");
    }

    // Update arguments for next iteration
    realTimeArguments["iteration"] = iteration;

    await Task.Delay(1000); // Wait 1 second between iterations
}

Console.WriteLine("✅ Real-time visualization completed");

3. Interactive Graph Inspection

Shows how to implement interactive graph inspection and debugging capabilities.

// Create interactive inspection workflow
var interactiveInspectionWorkflow = new GraphExecutor("InteractiveInspectionWorkflow", "Interactive graph inspection", logger);

// Configure interactive inspection
var interactiveInspectionOptions = new GraphVisualizationOptions
{
    EnableInteractiveInspection = true,
    EnableBreakpoints = true,
    EnableExecutionPause = true,
    EnableStepThrough = true,
    EnableStateInspection = true,
    EnableNodeInspection = true,
    ExportPath = "./interactive-exports"
};

interactiveInspectionWorkflow.ConfigureVisualization(interactiveInspectionOptions);

// Interactive processing node
var interactiveProcessor = new FunctionGraphNode(
    "interactive-processor",
    "Process with interactive inspection",
    async (context) =>
    {
        var inputData = context.GetValue<string>("input_data");
        var inspectionMode = context.GetValue<string>("inspection_mode", "normal");

        // Check for breakpoint
        if (inspectionMode == "breakpoint")
        {
            context.SetValue("breakpoint_hit", true);
            context.SetValue("breakpoint_data", inputData);
            context.SetValue("node_state", "paused");

            // Simulate breakpoint pause
            await Task.Delay(2000);
        }

        var processedData = $"Interactive processed: {inputData}";

        context.SetValue("processed_data", processedData);
        context.SetValue("processing_step", "interactive_processed");
        context.SetValue("node_state", "completed");

        return processedData;
    });

// Interactive inspector
var interactiveInspector = new FunctionGraphNode(
    "interactive-inspector",
    "Provide interactive inspection capabilities",
    async (context) =>
    {
        var processedData = context.GetValue<string>("processed_data");
        var inspectionMode = context.GetValue<string>("inspection_mode");
        var breakpointHit = context.GetValue<bool>("breakpoint_hit", false);

        // Interactive inspection logic
        var inspectionResults = new Dictionary<string, object>
        {
            ["node_id"] = "interactive-processor",
            ["input_data"] = context.GetValue<string>("input_data"),
            ["processed_data"] = processedData,
            ["inspection_mode"] = inspectionMode,
            ["breakpoint_hit"] = breakpointHit,
            ["node_state"] = context.GetValue<string>("node_state"),
            ["execution_time"] = DateTime.UtcNow,
            ["inspection_available"] = true
        };

        if (breakpointHit)
        {
            inspectionResults["breakpoint_data"] = context.GetValue<string>("breakpoint_data");
            inspectionResults["pause_duration"] = "2 seconds";
        }

        context.SetValue("inspection_results", inspectionResults);

        return $"Interactive inspection completed for {inspectionMode} mode";
    });

// Add nodes to interactive workflow
interactiveInspectionWorkflow.AddNode(interactiveProcessor);
interactiveInspectionWorkflow.AddNode(interactiveInspector);

// Set start node
interactiveInspectionWorkflow.SetStartNode(interactiveProcessor.NodeId);

// Test interactive inspection
var inspectionTestScenarios = new[]
{
    new { Data = "Normal processing", Mode = "normal" },
    new { Data = "Breakpoint processing", Mode = "breakpoint" },
    new { Data = "Step-through processing", Mode = "step" }
};

foreach (var scenario in inspectionTestScenarios)
{
    var arguments = new KernelArguments
    {
        ["input_data"] = scenario.Data,
        ["inspection_mode"] = scenario.Mode
    };

    Console.WriteLine($"🔍 Testing interactive inspection: {scenario.Data}");
    Console.WriteLine($"   Inspection Mode: {scenario.Mode}");

    var result = await interactiveInspectionWorkflow.ExecuteAsync(kernel, arguments);

    var inspectionResults = result.GetValue<Dictionary<string, object>>("inspection_results");
    var breakpointHit = result.GetValue<bool>("breakpoint_hit", false);

    if (inspectionResults != null)
    {
        Console.WriteLine($"   Node State: {inspectionResults["node_state"]}");
        Console.WriteLine($"   Breakpoint Hit: {breakpointHit}");

        if (breakpointHit)
        {
            Console.WriteLine($"   Breakpoint Data: {inspectionResults["breakpoint_data"]}");
            Console.WriteLine($"   Pause Duration: {inspectionResults["pause_duration"]}");
        }
    }

    Console.WriteLine();
}

4. Advanced Visualization Features

Demonstrates advanced visualization features including custom styling and export options.

// Create advanced visualization workflow
var advancedVisualizationWorkflow = new GraphExecutor("AdvancedVisualizationWorkflow", "Advanced visualization features", logger);

// Configure advanced visualization
var advancedVisualizationOptions = new GraphVisualizationOptions
{
    EnableDOTExport = true,
    EnableJSONExport = true,
    EnableMermaidExport = true,
    EnableRealTimeHighlights = true,
    EnableExecutionOverlays = true,
    EnableCustomStyling = true,
    EnableThemeSupport = true,
    EnableExportCompression = true,
    ExportPath = "./advanced-exports",
    CustomStyles = new Dictionary<string, string>
    {
        ["node_color"] = "#4CAF50",
        ["edge_color"] = "#2196F3",
        ["highlight_color"] = "#FF9800",
        ["error_color"] = "#F44336"
    },
    ExportFormats = new[] { "dot", "json", "mermaid", "svg", "png" }
};

advancedVisualizationWorkflow.ConfigureVisualization(advancedVisualizationOptions);

// Advanced processing node with custom styling
var advancedProcessor = new FunctionGraphNode(
    "advanced-processor",
    "Advanced processing with custom styling",
    async (context) =>
    {
        var inputData = context.GetValue<string>("input_data");
        var processingType = context.GetValue<string>("processing_type", "standard");

        // Apply custom styling based on processing type
        var nodeStyle = processingType switch
        {
            "priority" => "priority_style",
            "error" => "error_style",
            "success" => "success_style",
            _ => "standard_style"
        };

        context.SetValue("node_style", nodeStyle);
        context.SetValue("processing_type", processingType);

        // Simulate processing
        await Task.Delay(Random.Shared.Next(200, 600));

        var processedData = $"Advanced processed: {inputData} ({processingType})";
        context.SetValue("processed_data", processedData);
        context.SetValue("processing_step", "advanced_processed");

        return processedData;
    });

// Advanced visualizer with custom export
var advancedVisualizer = new FunctionGraphNode(
    "advanced-visualizer",
    "Advanced visualization with custom export",
    async (context) =>
    {
        var processedData = context.GetValue<string>("processed_data");
        var processingType = context.GetValue<string>("processing_type");
        var nodeStyle = context.GetValue<string>("node_style");

        // Generate custom visualization
        var customVisualization = new Dictionary<string, object>
        {
            ["node_styles"] = new Dictionary<string, object>
            {
                ["advanced-processor"] = new
                {
                    Style = nodeStyle,
                    Color = GetStyleColor(nodeStyle),
                    BorderWidth = GetStyleBorderWidth(nodeStyle),
                    Shape = GetStyleShape(nodeStyle)
                }
            },
            ["processing_metadata"] = new
            {
                Type = processingType,
                Timestamp = DateTime.UtcNow,
                Style = nodeStyle
            }
        };

        context.SetValue("custom_visualization", customVisualization);

        // Export with custom styling
        var styledDOT = await advancedVisualizationWorkflow.ExportToDOTAsync(customVisualization);
        var styledJSON = await advancedVisualizationWorkflow.ExportToJSONAsync(customVisualization);
        var styledMermaid = await advancedVisualizationWorkflow.ExportToMermaidAsync(customVisualization);

        // Save styled exports
        var timestamp = DateTime.UtcNow.ToString("yyyyMMdd_HHmmss");
        File.WriteAllText($"./advanced-exports/styled_{timestamp}.dot", styledDOT);
        File.WriteAllText($"./advanced-exports/styled_{timestamp}.json", styledJSON);
        File.WriteAllText($"./advanced-exports/styled_{timestamp}.mmd", styledMermaid);

        return $"Advanced visualization completed with {processingType} styling";
    });

// Add nodes to advanced workflow
advancedVisualizationWorkflow.AddNode(advancedProcessor);
advancedVisualizationWorkflow.AddNode(advancedVisualizer);

// Set start node
advancedVisualizationWorkflow.SetStartNode(advancedProcessor.NodeId);

// Test advanced visualization
var advancedTestScenarios = new[]
{
    new { Data = "Standard processing", Type = "standard" },
    new { Data = "Priority processing", Type = "priority" },
    new { Data = "Success processing", Type = "success" },
    new { Data = "Error processing", Type = "error" }
};

foreach (var scenario in advancedTestScenarios)
{
    var arguments = new KernelArguments
    {
        ["input_data"] = scenario.Data,
        ["processing_type"] = scenario.Type
    };

    Console.WriteLine($"🎨 Testing advanced visualization: {scenario.Data}");
    Console.WriteLine($"   Processing Type: {scenario.Type}");

    var result = await advancedVisualizationWorkflow.ExecuteAsync(kernel, arguments);

    var customVisualization = result.GetValue<Dictionary<string, object>>("custom_visualization");
    var nodeStyle = result.GetValue<string>("node_style");

    if (customVisualization != null)
    {
        var metadata = customVisualization["processing_metadata"] as dynamic;
        Console.WriteLine($"   Node Style: {nodeStyle}");
        Console.WriteLine($"   Style Color: {GetStyleColor(nodeStyle)}");
        Console.WriteLine($"   Export Files: styled_{DateTime.UtcNow:yyyyMMdd_HHmmss}.*");
    }

    Console.WriteLine();
}

// Helper methods for custom styling
string GetStyleColor(string style) => style switch
{
    "priority_style" => "#FF9800",
    "error_style" => "#F44336",
    "success_style" => "#4CAF50",
    _ => "#2196F3"
};

int GetStyleBorderWidth(string style) => style switch
{
    "priority_style" => 3,
    "error_style" => 2,
    "success_style" => 2,
    _ => 1
};

string GetStyleShape(string style) => style switch
{
    "priority_style" => "diamond",
    "error_style" => "octagon",
    "success_style" => "ellipse",
    _ => "box"
};

Expected Output

Basic Graph Visualization Example

📊 Exporting graph in different formats...
   DOT Export: 1,234 characters
   JSON Export: 2,345 characters
   Mermaid Export: 1,567 characters

🧪 Testing visualization workflow: Sample data 1
   Processing Step: output_generated
   Final Output: Final Output: Transformed: Processed: Sample data 1

Real-Time Execution Visualization Example

🎬 Starting real-time visualization...
   Visualization will update every 500ms
   Exports will be saved to ./real-time-exports/
   Iteration 1: Data: Real-time processed: Real-time test data (iteration 1), Progress: 10%
   Iteration 2: Data: Real-time processed: Real-time test data (iteration 2), Progress: 20%
✅ Real-time visualization completed

Interactive Graph Inspection Example

🔍 Testing interactive inspection: Normal processing
   Inspection Mode: normal
   Node State: completed
   Breakpoint Hit: False

🔍 Testing interactive inspection: Breakpoint processing
   Inspection Mode: breakpoint
   Node State: completed
   Breakpoint Hit: True
   Breakpoint Data: Breakpoint processing
   Pause Duration: 2 seconds

Advanced Visualization Example

🎨 Testing advanced visualization: Priority processing
   Processing Type: priority
   Node Style: priority_style
   Style Color: #FF9800
   Export Files: styled_20250801_143022.*

Configuration Options

Visualization Configuration

var visualizationOptions = new GraphVisualizationOptions
{
    EnableDOTExport = true,                           // Enable DOT format export
    EnableJSONExport = true,                          // Enable JSON format export
    EnableMermaidExport = true,                       // Enable Mermaid format export
    EnableRealTimeHighlights = true,                  // Enable real-time execution highlights
    EnableExecutionOverlays = true,                   // Enable execution flow overlays
    EnableInteractiveInspection = true,               // Enable interactive inspection
    EnableBreakpoints = true,                         // Enable execution breakpoints
    EnableExecutionPause = true,                      // Enable execution pausing
    EnableStepThrough = true,                         // Enable step-through execution
    EnableStateInspection = true,                     // Enable state inspection
    EnableNodeInspection = true,                      // Enable node-level inspection
    EnableCustomStyling = true,                       // Enable custom node/edge styling
    EnableThemeSupport = true,                        // Enable theme support
    EnableExportCompression = true,                   // Enable export compression
    EnableLiveUpdates = true,                         // Enable live visualization updates
    EnableExecutionTracking = true,                   // Enable execution path tracking
    EnableNodeStateHighlighting = true,               // Enable node state highlighting
    UpdateInterval = TimeSpan.FromMilliseconds(500),  // Update interval for real-time
    ExportPath = "./graph-exports",                   // Export directory path
    ExportFormats = new[] { "dot", "json", "mermaid", "svg", "png" }, // Supported formats
    CustomStyles = new Dictionary<string, string>     // Custom styling options
    {
        ["node_color"] = "#4CAF50",
        ["edge_color"] = "#2196F3",
        ["highlight_color"] = "#FF9800",
        ["error_color"] = "#F44336"
    }
};

Real-Time Visualization Configuration

var realTimeOptions = new RealTimeVisualizationOptions
{
    EnableLiveUpdates = true,                         // Enable live visualization updates
    UpdateInterval = TimeSpan.FromMilliseconds(500),  // Update frequency
    EnableExecutionTracking = true,                   // Track execution paths
    EnableNodeStateHighlighting = true,               // Highlight node states
    EnableProgressIndicators = true,                  // Show execution progress
    EnableTimelineView = true,                        // Show execution timeline
    EnablePerformanceMetrics = true,                  // Show performance metrics
    MaxHistorySize = 1000,                            // Maximum history size
    EnableAutoExport = true,                          // Auto-export on updates
    ExportOnCompletion = true,                        // Export when execution completes
    EnableAnimation = true,                           // Enable smooth animations
    AnimationDuration = TimeSpan.FromMilliseconds(300) // Animation duration
};

Troubleshooting

Common Issues

Visualization Not Working

# Problem: Graph visualization is not working
# Solution: Check visualization configuration and enable required features
EnableDOTExport = true;
EnableRealTimeHighlights = true;
ExportPath = "./valid-path";

Export Failures

# Problem: Graph export is failing
# Solution: Check export path and permissions
ExportPath = "./graph-exports";
Directory.CreateDirectory(ExportPath); // Ensure directory exists

Real-Time Updates Not Working

# Problem: Real-time updates are not working
# Solution: Enable real-time features and check update interval
EnableRealTimeHighlights = true;
EnableLiveUpdates = true;
UpdateInterval = TimeSpan.FromMilliseconds(500);

Debug Mode

Enable detailed logging for troubleshooting:

// Enable debug logging
var logger = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
    builder.SetMinimumLevel(LogLevel.Debug);
}).CreateLogger<GraphVisualizationExample>();

// Configure visualization with debug logging
var debugVisualizationOptions = new GraphVisualizationOptions
{
    EnableDOTExport = true,
    EnableJSONExport = true,
    EnableRealTimeHighlights = true,
    EnableDebugLogging = true,
    LogVisualizationUpdates = true,
    LogExportOperations = true
};

Advanced Patterns

Custom Visualization Styles

// Implement custom visualization styles
public class CustomVisualizationStyle : IVisualizationStyle
{
    public async Task<Dictionary<string, object>> ApplyStyleAsync(GraphNode node, GraphState state)
    {
        var customStyle = new Dictionary<string, object>();

        // Apply custom styling based on node type
        switch (node.NodeType)
        {
            case "FunctionGraphNode":
                customStyle["shape"] = "box";
                customStyle["color"] = "#4CAF50";
                customStyle["style"] = "filled";
                break;
            case "ConditionalGraphNode":
                customStyle["shape"] = "diamond";
                customStyle["color"] = "#2196F3";
                customStyle["style"] = "filled";
                break;
            case "ReActLoopGraphNode":
                customStyle["shape"] = "ellipse";
                customStyle["color"] = "#FF9800";
                customStyle["style"] = "filled";
                break;
        }

        // Apply state-based styling
        if (state.GetValue<bool>("is_error", false))
        {
            customStyle["color"] = "#F44336";
            customStyle["style"] = "filled,diagonals";
        }

        return customStyle;
    }
}

Custom Export Formats

// Implement custom export format
public class CustomExportFormat : IGraphExportFormat
{
    public string FormatName => "custom";
    public string FileExtension => ".custom";

    public async Task<string> ExportAsync(GraphExecutor executor, Dictionary<string, object> options = null)
    {
        var customExport = new StringBuilder();

        // Generate custom format
        customExport.AppendLine("CUSTOM GRAPH EXPORT");
        customExport.AppendLine("==================");
        customExport.AppendLine();

        foreach (var node in executor.Nodes)
        {
            customExport.AppendLine($"Node: {node.NodeId}");
            customExport.AppendLine($"  Type: {node.NodeType}");
            customExport.AppendLine($"  Description: {node.Description}");
            customExport.AppendLine();
        }

        return customExport.ToString();
    }
}

Interactive Debugging

// Implement interactive debugging
public class InteractiveDebugger : IGraphDebugger
{
    private readonly Dictionary<string, Breakpoint> _breakpoints = new();

    public async Task<bool> ShouldPauseAsync(GraphNode node, GraphState state)
    {
        if (_breakpoints.TryGetValue(node.NodeId, out var breakpoint))
        {
            return await breakpoint.EvaluateAsync(node, state);
        }
        return false;
    }

    public async Task<DebugAction> HandleBreakpointAsync(GraphNode node, GraphState state)
    {
        Console.WriteLine($"🔴 Breakpoint hit at node: {node.NodeId}");
        Console.WriteLine($"   Current state: {string.Join(", ", state.Keys)}");

        Console.WriteLine("Debug commands: [c]ontinue, [s]tep, [i]nspect, [q]uit");
        var command = Console.ReadLine()?.ToLower();

        return command switch
        {
            "c" => DebugAction.Continue,
            "s" => DebugAction.Step,
            "i" => await InspectStateAsync(state),
            "q" => DebugAction.Quit,
            _ => DebugAction.Continue
        };
    }

    private async Task<DebugAction> InspectStateAsync(GraphState state)
    {
        Console.WriteLine("📊 State inspection:");
        foreach (var kvp in state)
        {
            Console.WriteLine($"   {kvp.Key}: {kvp.Value}");
        }
        return DebugAction.Continue;
    }
}

See Also