// // SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // "use client" import React, { useState, useEffect, useRef } from 'react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Badge } from '@/components/ui/badge' import { Switch } from '@/components/ui/switch' import { Loader2, Zap, Activity, Cpu, MonitorSpeaker } from 'lucide-react' import { useToast } from '@/hooks/use-toast' interface GraphData { nodes: Array<{ id: string name: string group?: string [key: string]: any }> links: Array<{ source: string target: string name: string [key: string]: any }> } interface LocalGPUViewerProps { graphData: GraphData onError?: (error: Error) => void } interface GPUCapabilities { has_rapids: boolean has_torch_geometric: boolean gpu_available: boolean supported_layouts: string[] supported_clustering: string[] } interface ProcessedData { nodes: any[] edges: any[] gpu_processed: boolean layout_positions: Record clusters: Record centrality: Record> stats: { node_count: number edge_count: number gpu_accelerated: boolean layout_computed: boolean clusters_computed: boolean centrality_computed: boolean } timestamp: string } export function LocalGPUViewer({ graphData, onError }: LocalGPUViewerProps) { const [isProcessing, setIsProcessing] = useState(false) const [processedData, setProcessedData] = useState(null) const [capabilities, setCapabilities] = useState(null) const [serviceHealth, setServiceHealth] = useState<'unknown' | 'healthy' | 'error'>('unknown') // Processing options const [layoutAlgorithm, setLayoutAlgorithm] = useState('force_atlas2') const [clusteringAlgorithm, setClusteringAlgorithm] = useState('leiden') const [gpuAcceleration, setGpuAcceleration] = useState(true) const [computeCentrality, setComputeCentrality] = useState(true) const { toast } = useToast() const wsRef = useRef(null) // Check service health and capabilities on mount useEffect(() => { checkServiceHealth() getCapabilities() setupWebSocket() return () => { if (wsRef.current) { wsRef.current.close() } } }, []) const checkServiceHealth = async () => { try { const response = await fetch('/api/local-gpu/health') if (response.ok) { setServiceHealth('healthy') } else { setServiceHealth('error') } } catch (error) { console.error('Local GPU service health check failed:', error) setServiceHealth('error') } } const getCapabilities = async () => { try { const response = await fetch('/api/local-gpu/capabilities') if (response.ok) { const caps = await response.json() setCapabilities(caps) // Set defaults based on capabilities if (caps.supported_layouts?.length > 0) { setLayoutAlgorithm(caps.supported_layouts[0]) } if (caps.supported_clustering?.length > 0) { setClusteringAlgorithm(caps.supported_clustering[0]) } } } catch (error) { console.error('Failed to get GPU capabilities:', error) } } const setupWebSocket = () => { try { const ws = new WebSocket('ws://localhost:8081/ws') ws.onopen = () => { console.log('WebSocket connected to local GPU service') } ws.onmessage = (event) => { try { const message = JSON.parse(event.data) if (message.type === 'graph_processed') { setProcessedData(message.data) setIsProcessing(false) toast({ title: "GPU Processing Complete", description: `Processed ${message.data.stats.node_count} nodes with ${message.data.gpu_processed ? 'GPU' : 'CPU'} acceleration.`, }) } } catch (error) { console.error('WebSocket message error:', error) } } ws.onerror = (error) => { console.error('WebSocket error:', error) } wsRef.current = ws } catch (error) { console.error('WebSocket setup failed:', error) } } const processWithLocalGPU = async () => { if (!graphData?.nodes?.length || !graphData?.links?.length) { toast({ title: "No Graph Data", description: "Please ensure graph data is loaded before processing.", variant: "destructive" }) return } setIsProcessing(true) try { const requestData = { graph_data: { nodes: graphData.nodes, links: graphData.links }, layout_algorithm: layoutAlgorithm, clustering_algorithm: clusteringAlgorithm, gpu_acceleration: gpuAcceleration, compute_centrality: computeCentrality } const response = await fetch('/api/local-gpu/process', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestData) }) if (!response.ok) { throw new Error(`Local GPU processing failed: ${response.statusText}`) } // The result will come via WebSocket, so we just wait toast({ title: "Processing Started", description: "Graph processing started on local GPU. Results will appear shortly.", }) } catch (error) { console.error('Local GPU processing error:', error) const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' toast({ title: "Processing Failed", description: errorMessage, variant: "destructive" }) setIsProcessing(false) if (onError) { onError(error instanceof Error ? error : new Error(errorMessage)) } } } const ServiceStatus = () => (
Local GPU Service: {serviceHealth === 'healthy' ? 'Connected' : serviceHealth === 'error' ? 'Disconnected' : 'Checking...'} {capabilities?.gpu_available && ( cuGraph Ready )}
) const StatsDisplay = ({ stats }: { stats: ProcessedData['stats'] }) => (
{stats.node_count.toLocaleString()} nodes
{stats.edge_count.toLocaleString()} edges
{stats.gpu_accelerated ? ( ) : ( )} {stats.gpu_accelerated ? 'GPU' : 'CPU'} processed
{stats.layout_computed && (
Layout computed
)} {stats.clusters_computed && (
Clusters detected
)} {stats.centrality_computed && (
Centrality computed
)}
) return (
{/* Service Status */} Local GPU Visualization {/* GPU Capabilities */} {capabilities && (

GPU Capabilities

RAPIDS cuGraph: {capabilities.has_rapids ? 'Available' : 'Not Available'}
PyTorch Geometric: {capabilities.has_torch_geometric ? 'Available' : 'Not Available'}
)} {/* Processing Controls */}
{/* Action Buttons */}
{/* Results */} {processedData && ( Local GPU Processing Results {/* Statistics */} {/* Visualization */}
🚀 Local GPU Visualization (cuGraph Powered)
Processing completed locally with {processedData.gpu_processed ? 'GPU' : 'CPU'} acceleration