chore: Regenerate all playbooks

This commit is contained in:
GitLab CI 2025-12-02 19:43:52 +00:00
parent 65d43d3ae6
commit 373591c46f
218 changed files with 7001 additions and 872 deletions

View File

@ -54,8 +54,9 @@ The setup includes:
- Document processing time scales with document size and complexity - Document processing time scales with document size and complexity
- **Rollback**: Stop and remove Docker containers, delete downloaded models if needed - **Rollback**: Stop and remove Docker containers, delete downloaded models if needed
- **Last Updated**: 11/19/2025 - **Last Updated**: 12/02/2025
- Updated GPU Visualization Service - Knowledge graph search with multi-hop graph traversal
- Improved UI/UX
## Instructions ## Instructions

View File

@ -1,3 +1,44 @@
# Dependencies
node_modules node_modules
.pnpm-store
**/node_modules
**/.pnpm-store
# Next.js build outputs
.next .next
out
**/.next
**/out
# Git
.git .git
.gitignore
**/.git
# Logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
# IDE
.vscode
.idea
*.swp
*.swo
.DS_Store
# Testing
coverage
__tests__
*.test.ts
*.test.tsx
*.spec.ts
*.spec.tsx
# Misc
.env.local
.env.*.local
README.md
.eslintcache

View File

@ -1,48 +1,74 @@
# Use the official Node.js image from the Docker Hub # Optimized multi-stage Dockerfile for faster builds
FROM node:18-slim # Stage 1: Dependencies
FROM node:18-slim AS deps
# Set environment variables to avoid interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
ENV NPM_CONFIG_YES=true
ENV PNPM_HOME=/pnpm
ENV PATH="$PNPM_HOME:$PATH"
# Set the working directory
WORKDIR /app WORKDIR /app
# Install pnpm globally with --force and yes flags # Install pnpm
RUN npm install -g pnpm --force --yes RUN npm install -g pnpm --force --yes
# Copy package files ONLY (for better Docker layer caching) # Copy dependency files
# Copy package.json (required) and pnpm-lock.yaml (optional) COPY ./frontend/package.json ./frontend/pnpm-lock.yaml* ./
COPY ./frontend/package.json ./
COPY ./frontend/pnpm-lock.yaml* ./
# Copy the scripts directory (needed for setup-pinecone)
COPY ./scripts/ /scripts/ COPY ./scripts/ /scripts/
# Update the setup-pinecone.js path in package.json # Update the setup-pinecone.js path
RUN sed -i 's|"setup-pinecone": "node ../scripts/setup-pinecone.js"|"setup-pinecone": "node /scripts/setup-pinecone.js"|g' package.json RUN sed -i 's|"setup-pinecone": "node ../scripts/setup-pinecone.js"|"setup-pinecone": "node /scripts/setup-pinecone.js"|g' package.json
# Install project dependencies (this layer will be cached if package files don't change) # Install dependencies with cache mount for faster rebuilds
# Use --no-frozen-lockfile as fallback if lockfile is missing or out of sync RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
RUN pnpm config set auto-install-peers true && \ pnpm config set auto-install-peers true && \
if [ -f pnpm-lock.yaml ]; then \ if [ -f pnpm-lock.yaml ]; then \
echo "Lock file found, installing with frozen lockfile..." && \ pnpm install --no-optional --frozen-lockfile || pnpm install --no-optional --no-frozen-lockfile; \
(pnpm install --no-optional --frozen-lockfile || pnpm install --no-optional --no-frozen-lockfile); \
else \ else \
echo "No lock file found, installing without frozen lockfile..." && \
pnpm install --no-optional --no-frozen-lockfile; \ pnpm install --no-optional --no-frozen-lockfile; \
fi fi
# Copy the rest of the frontend files # Stage 2: Builder
FROM node:18-slim AS builder
WORKDIR /app
# Install pnpm
RUN npm install -g pnpm --force --yes
# Copy node_modules from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/package.json ./package.json
COPY --from=deps /scripts /scripts
# Copy source code
COPY ./frontend/ ./ COPY ./frontend/ ./
# Build the application # Set build environment variables
RUN pnpm build ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_ENV production
# Build with cache mount for Next.js cache
RUN --mount=type=cache,target=/app/.next/cache \
pnpm build
# Stage 3: Production runtime
FROM node:18-slim AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
# Create non-root user for security
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Copy necessary files from builder
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Create data directory for query logs with proper permissions
RUN mkdir -p /app/data && chown -R nextjs:nodejs /app/data
USER nextjs
# Expose the port the app runs on
EXPOSE 3000 EXPOSE 3000
# Start the application ENV PORT 3000
CMD ["pnpm", "start"] ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]

View File

@ -1,3 +1,19 @@
//
// 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.
//
// ArangoDB initialization script to create the txt2kg database // ArangoDB initialization script to create the txt2kg database
// This script is executed automatically when the ArangoDB container starts // This script is executed automatically when the ArangoDB container starts

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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.
#
set -e set -e
# Wait for ArangoDB to be ready # Wait for ArangoDB to be ready

View File

@ -1,4 +1,20 @@
#!/bin/sh #!/bin/sh
#
# 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.
#
# Script to initialize Pinecone index at container startup # Script to initialize Pinecone index at container startup
echo "Initializing Pinecone index..." echo "Initializing Pinecone index..."

View File

@ -0,0 +1,68 @@
#!/bin/sh
#
# 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.
#
# Script to initialize Qdrant collection at container startup
echo "Initializing Qdrant collection..."
# Wait for the Qdrant service to become available
echo "Waiting for Qdrant service to start..."
max_attempts=30
attempt=1
while [ $attempt -le $max_attempts ]; do
if curl -s http://qdrant:6333/healthz > /dev/null; then
echo "Qdrant service is up!"
break
fi
echo "Waiting for Qdrant service (attempt $attempt/$max_attempts)..."
attempt=$((attempt + 1))
sleep 2
done
if [ $attempt -gt $max_attempts ]; then
echo "Timed out waiting for Qdrant service"
exit 1
fi
# Check if collection already exists
echo "Checking if collection 'entity-embeddings' exists..."
COLLECTION_EXISTS=$(curl -s http://qdrant:6333/collections/entity-embeddings | grep -c '"status":"ok"' || echo "0")
if [ "$COLLECTION_EXISTS" -gt "0" ]; then
echo "Collection 'entity-embeddings' already exists, skipping creation"
else
# Create the collection
echo "Creating collection 'entity-embeddings'..."
curl -X PUT "http://qdrant:6333/collections/entity-embeddings" \
-H "Content-Type: application/json" \
-d '{
"vectors": {
"size": 384,
"distance": "Cosine"
}
}'
if [ $? -eq 0 ]; then
echo "✅ Collection 'entity-embeddings' created successfully"
else
echo "❌ Failed to create collection"
exit 1
fi
fi
echo "Qdrant initialization complete"

View File

@ -1,3 +1,4 @@
services: services:
app: app:
build: build:
@ -8,12 +9,18 @@ services:
environment: environment:
- ARANGODB_URL=http://arangodb:8529 - ARANGODB_URL=http://arangodb:8529
- ARANGODB_DB=txt2kg - ARANGODB_DB=txt2kg
- QDRANT_URL=http://qdrant:6333
- VECTOR_DB_TYPE=qdrant
- LANGCHAIN_TRACING_V2=true - LANGCHAIN_TRACING_V2=true
- SENTENCE_TRANSFORMER_URL=http://sentence-transformers:80
- MODEL_NAME=all-MiniLM-L6-v2
- EMBEDDINGS_API_URL=http://sentence-transformers:80
- GRPC_SSL_CIPHER_SUITES=HIGH+ECDSA:HIGH+aRSA - GRPC_SSL_CIPHER_SUITES=HIGH+ECDSA:HIGH+aRSA
- NODE_TLS_REJECT_UNAUTHORIZED=0 - NODE_TLS_REJECT_UNAUTHORIZED=0
- OLLAMA_BASE_URL=http://ollama:11434/v1 - OLLAMA_BASE_URL=http://ollama:11434/v1
- OLLAMA_MODEL=llama3.1:8b - OLLAMA_MODEL=llama3.1:8b
- REMOTE_WEBGPU_SERVICE_URL=http://txt2kg-remote-webgpu:8083 - REMOTE_WEBGPU_SERVICE_URL=http://txt2kg-remote-webgpu:8083
- NVIDIA_API_KEY=${NVIDIA_API_KEY:-}
# Node.js timeout configurations for large model processing # Node.js timeout configurations for large model processing
- NODE_OPTIONS=--max-http-header-size=80000 - NODE_OPTIONS=--max-http-header-size=80000
- UV_THREADPOOL_SIZE=128 - UV_THREADPOOL_SIZE=128
@ -22,9 +29,12 @@ services:
networks: networks:
- default - default
- txt2kg-network - txt2kg-network
- pinecone-net
depends_on: depends_on:
- arangodb - arangodb
- ollama - ollama
# Optional: sentence-transformers and entity-embeddings are only needed for vector search
# Traditional graph search works without these services
arangodb: arangodb:
image: arangodb:latest image: arangodb:latest
ports: ports:
@ -58,16 +68,13 @@ services:
volumes: volumes:
- ollama_data:/root/.ollama - ollama_data:/root/.ollama
environment: environment:
- NVIDIA_VISIBLE_DEVICES=all # Make all GPUs visible to the container
- NVIDIA_DRIVER_CAPABILITIES=compute,utility # Required capabilities for CUDA
- OLLAMA_FLASH_ATTENTION=1 # Enable flash attention for better performance - OLLAMA_FLASH_ATTENTION=1 # Enable flash attention for better performance
- OLLAMA_KEEP_ALIVE=30m # Keep models loaded for 30 minutes - OLLAMA_KEEP_ALIVE=30m # Keep models loaded for 30 minutes
- OLLAMA_CUDA=1 # Enable CUDA acceleration - OLLAMA_NUM_PARALLEL=4 # Process 4 requests in parallel - DGX Spark has unified memory
- OLLAMA_LLM_LIBRARY=cuda # Use CUDA library for LLM operations
- OLLAMA_NUM_PARALLEL=1 # Process one request at a time for 70B models
- OLLAMA_MAX_LOADED_MODELS=1 # Load only one model at a time to avoid VRAM contention - OLLAMA_MAX_LOADED_MODELS=1 # Load only one model at a time to avoid VRAM contention
- OLLAMA_KV_CACHE_TYPE=q8_0 # Reduce KV cache VRAM usage with minimal performance impact - OLLAMA_KV_CACHE_TYPE=q8_0 # Reduce KV cache VRAM usage with minimal performance impact
- OLLAMA_GPU_LAYERS=999 # Use maximum GPU layers
- OLLAMA_GPU_MEMORY_FRACTION=0.9 # Use 90% of GPU memory
- CUDA_VISIBLE_DEVICES=0 # Use GPU 0 (change to 'all' for multi-GPU)
networks: networks:
- default - default
restart: unless-stopped restart: unless-stopped
@ -79,19 +86,90 @@ services:
count: all count: all
capabilities: [gpu] capabilities: [gpu]
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"] test: ["CMD", "ollama", "list"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
start_period: 60s start_period: 60s
# Optional services for vector search (NOT required for traditional graph search)
# Traditional graph search works with just: app, arangodb, and ollama
sentence-transformers:
build:
context: ../services/sentence-transformers
dockerfile: Dockerfile
ports:
- '8000:80'
environment:
- MODEL_NAME=all-MiniLM-L6-v2
networks:
- default
restart: unless-stopped
profiles:
- vector-search # Only start with: docker compose --profile vector-search up
qdrant:
image: qdrant/qdrant:latest
container_name: qdrant
ports:
- "6333:6333"
- "6334:6334"
volumes:
- qdrant_data:/qdrant/storage
networks:
- pinecone-net
restart: unless-stopped
profiles:
- vector-search # Only start with: docker compose --profile vector-search up
qdrant-init:
image: curlimages/curl:latest
depends_on:
- qdrant
restart: "no"
entrypoint: /bin/sh
command:
- -c
- |
echo 'Waiting for Qdrant to start...'
sleep 5
echo 'Checking if entity-embeddings collection exists...'
RESPONSE=$(curl -s http://qdrant:6333/collections/entity-embeddings)
if echo "$RESPONSE" | grep -q '"status":"ok"'; then
echo 'entity-embeddings collection already exists'
else
echo 'Creating collection entity-embeddings...'
curl -X PUT http://qdrant:6333/collections/entity-embeddings \
-H 'Content-Type: application/json' \
-d '{"vectors":{"size":384,"distance":"Cosine"}}'
echo ''
echo 'entity-embeddings collection created successfully'
fi
echo 'Checking if document-embeddings collection exists...'
RESPONSE=$(curl -s http://qdrant:6333/collections/document-embeddings)
if echo "$RESPONSE" | grep -q '"status":"ok"'; then
echo 'document-embeddings collection already exists'
else
echo 'Creating collection document-embeddings...'
curl -X PUT http://qdrant:6333/collections/document-embeddings \
-H 'Content-Type: application/json' \
-d '{"vectors":{"size":384,"distance":"Cosine"}}'
echo ''
echo 'document-embeddings collection created successfully'
fi
networks:
- pinecone-net
profiles:
- vector-search
volumes: volumes:
arangodb_data: arangodb_data:
arangodb_apps_data: arangodb_apps_data:
ollama_data: ollama_data:
qdrant_data:
networks: networks:
default: default:
driver: bridge driver: bridge
txt2kg-network: txt2kg-network:
driver: bridge driver: bridge
pinecone-net:
name: pinecone

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
import os import os
import torch import torch

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
import requests import requests
import json import json

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
import os import os
import argparse import argparse

View File

@ -23,6 +23,7 @@ RUN pip install --no-cache-dir -r requirements.txt
# Copy the service code # Copy the service code
COPY unified_gpu_service.py . COPY unified_gpu_service.py .
COPY pygraphistry_service.py .
# Create a non-root user for security (using a different UID to avoid conflicts) # Create a non-root user for security (using a different UID to avoid conflicts)
RUN useradd -m -u 1001 appuser && chown -R appuser:appuser /app RUN useradd -m -u 1001 appuser && chown -R appuser:appuser /app

View File

@ -9,11 +9,12 @@ This directory contains optional GPU-accelerated graph visualization services th
## 📦 Available Services ## 📦 Available Services
### 1. Unified GPU Service (`unified_gpu_service.py`) ### 1. Unified GPU Service (`unified_gpu_service.py`)
Provides **Local GPU (cuGraph)** and **CPU (NetworkX)** processing in a single FastAPI service. Combines **PyGraphistry Cloud** and **Local GPU (cuGraph)** processing into a single FastAPI service.
**Processing Modes:** **Processing Modes:**
| Mode | Description | Requirements | | Mode | Description | Requirements |
|------|-------------|--------------| |------|-------------|--------------|
| **PyGraphistry Cloud** | Interactive GPU embeds in browser | API credentials |
| **Local GPU (cuGraph)** | Full GPU processing on your hardware | NVIDIA GPU + cuGraph | | **Local GPU (cuGraph)** | Full GPU processing on your hardware | NVIDIA GPU + cuGraph |
| **Local CPU** | NetworkX fallback processing | None | | **Local CPU** | NetworkX fallback processing | None |
@ -26,8 +27,9 @@ Local GPU processing service with WebSocket support for real-time updates.
## 🛠️ Setup ## 🛠️ Setup
### Prerequisites ### Prerequisites
- NVIDIA GPU with CUDA support (for GPU mode) - NVIDIA GPU with CUDA support (for GPU modes)
- RAPIDS cuGraph (for local GPU processing) - RAPIDS cuGraph (for local GPU processing)
- PyGraphistry account (for cloud mode)
### Installation ### Installation
@ -92,8 +94,9 @@ Response:
```json ```json
{ {
"processing_modes": { "processing_modes": {
"local_gpu": {"available": true, "description": "Local GPU processing with cuGraph/RAPIDS"}, "pygraphistry_cloud": {"available": true, "description": "..."},
"local_cpu": {"available": true, "description": "Local CPU fallback processing with NetworkX"} "local_gpu": {"available": true, "description": "..."},
"local_cpu": {"available": true, "description": "..."}
}, },
"has_rapids": true, "has_rapids": true,
"gpu_available": true "gpu_available": true
@ -105,18 +108,33 @@ Response:
The txt2kg frontend includes built-in components for GPU visualization: The txt2kg frontend includes built-in components for GPU visualization:
- `UnifiedGPUViewer`: Connects to unified GPU service - `UnifiedGPUViewer`: Connects to unified GPU service
- `PyGraphistryViewer`: Direct PyGraphistry cloud integration
- `ForceGraphWrapper`: Three.js WebGPU visualization (default) - `ForceGraphWrapper`: Three.js WebGPU visualization (default)
### Using GPU Services in Frontend ### Using GPU Services in Frontend
The frontend has API routes that can connect to these services: The frontend has API routes that can connect to these services:
- `/api/pygraphistry/*`: PyGraphistry integration
- `/api/unified-gpu/*`: Unified GPU service integration - `/api/unified-gpu/*`: Unified GPU service integration
To use these services, ensure they are running separately and configure the frontend environment variables accordingly. To use these services, ensure they are running separately and configure the frontend environment variables accordingly.
### Processing Graph Data ### Mode-Specific Processing
```javascript ```javascript
// PyGraphistry Cloud mode
const response = await fetch('/api/unified-gpu/visualize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
graph_data: { nodes, links },
processing_mode: 'pygraphistry_cloud',
layout_type: 'force',
clustering: true,
gpu_acceleration: true
})
})
// Local GPU mode // Local GPU mode
const response = await fetch('/api/unified-gpu/visualize', { const response = await fetch('/api/unified-gpu/visualize', {
method: 'POST', method: 'POST',
@ -129,20 +147,15 @@ const response = await fetch('/api/unified-gpu/visualize', {
compute_centrality: true compute_centrality: true
}) })
}) })
// Local CPU mode
const response = await fetch('/api/unified-gpu/visualize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
graph_data: { nodes, links },
processing_mode: 'local_cpu'
})
})
``` ```
## 🔧 Configuration Options ## 🔧 Configuration Options
### PyGraphistry Cloud Mode
- `layout_type`: "force", "circular", "hierarchical"
- `gpu_acceleration`: true/false
- `clustering`: true/false
### Local GPU Mode ### Local GPU Mode
- `layout_algorithm`: "force_atlas2", "spectral", "fruchterman_reingold" - `layout_algorithm`: "force_atlas2", "spectral", "fruchterman_reingold"
- `clustering_algorithm`: "leiden", "louvain", "spectral" - `clustering_algorithm`: "leiden", "louvain", "spectral"
@ -159,7 +172,8 @@ const response = await fetch('/api/unified-gpu/visualize', {
"processed_nodes": [...], "processed_nodes": [...],
"processed_edges": [...], "processed_edges": [...],
"processing_mode": "local_gpu", "processing_mode": "local_gpu",
"layout_positions": {...}, "embed_url": "https://hub.graphistry.com/...", // Only for cloud mode
"layout_positions": {...}, // Only for local GPU mode
"clusters": {...}, "clusters": {...},
"centrality": {...}, "centrality": {...},
"stats": { "stats": {
@ -173,17 +187,18 @@ const response = await fetch('/api/unified-gpu/visualize', {
} }
``` ```
## 🚀 Benefits ## 🚀 Benefits of Unified Approach
### ✅ Advantages ### ✅ Advantages
- **Single service** - One port, one deployment - **Single service** - One port, one deployment
- **Mode switching** - Choose best processing per graph - **Mode switching** - Choose best processing per graph
- **Fallback handling** - Graceful degradation if GPU unavailable - **Fallback handling** - Graceful degradation if GPU unavailable
- **Consistent API** - Same interface for all modes - **Consistent API** - Same interface for all modes
- **No GPL dependencies** - All dependencies are permissively licensed - **Better testing** - Easy comparison between modes
### 🎯 Use Cases ### 🎯 Use Cases
- **Local GPU**: Private data, large-scale processing, GPU-accelerated algorithms - **PyGraphistry Cloud**: Sharing visualizations, demos, production embeds
- **Local GPU**: Private data, large-scale processing, custom algorithms
- **Local CPU**: Development, testing, small graphs - **Local CPU**: Development, testing, small graphs
## 🐛 Troubleshooting ## 🐛 Troubleshooting
@ -197,6 +212,16 @@ nvidia-smi
python -c "import cudf, cugraph; print('RAPIDS OK')" python -c "import cudf, cugraph; print('RAPIDS OK')"
``` ```
### PyGraphistry Credentials
```bash
# Verify credentials are set
echo $GRAPHISTRY_PERSONAL_KEY
echo $GRAPHISTRY_SECRET_KEY
# Test connection
python -c "import graphistry; graphistry.register(personal_key_id='$GRAPHISTRY_PERSONAL_KEY', personal_key_secret='$GRAPHISTRY_SECRET_KEY'); print('PyGraphistry OK')"
```
### Service Health ### Service Health
```bash ```bash
curl http://localhost:8080/api/health curl http://localhost:8080/api/health
@ -204,13 +229,7 @@ curl http://localhost:8080/api/health
## 📈 Performance Tips ## 📈 Performance Tips
1. **Large graphs (>100k nodes)**: Use `local_gpu` mode with RAPIDS cuGraph 1. **Large graphs (>100k nodes)**: Use `local_gpu` mode
2. **Development**: Use `local_cpu` mode for speed and simplicity 2. **Sharing/demos**: Use `pygraphistry_cloud` mode
3. **Mixed workloads**: Switch modes dynamically based on graph size and GPU availability 3. **Development**: Use `local_cpu` mode for speed
4. **Mixed workloads**: Switch modes dynamically based on graph size
## 📝 License Compliance
This service has been updated to remove all GPL-licensed dependencies:
- ❌ Removed: `igraph` (GPL v2+)
- ❌ Removed: `graphistry` with `compute_igraph` (uses igraph internally)
- ✅ Uses only: NetworkX (BSD), cuGraph (Apache 2.0), NumPy (BSD), pandas (BSD)

View File

@ -1,3 +1,19 @@
#
# 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.
#
import os import os
import json import json
import numpy as np import numpy as np

View File

@ -0,0 +1,728 @@
#
# 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.
#
import graphistry
import pandas as pd
import numpy as np
from typing import Dict, List, Any, Optional
import asyncio
import json
from datetime import datetime
import logging
from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel
import uvicorn
import os
import time
from concurrent.futures import ThreadPoolExecutor
import networkx as nx
from enum import Enum
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Initialize PyGraphistry
def init_graphistry():
"""Initialize PyGraphistry with GPU acceleration"""
try:
# Set up authentication - check for different credential types
api_key = os.getenv('GRAPHISTRY_API_KEY')
personal_key = os.getenv('GRAPHISTRY_PERSONAL_KEY')
secret_key = os.getenv('GRAPHISTRY_SECRET_KEY')
username = os.getenv('GRAPHISTRY_USERNAME')
password = os.getenv('GRAPHISTRY_PASSWORD')
if personal_key and secret_key:
# Configure for cloud API with personal key and secret
graphistry.register(
api=3,
protocol="https",
server="hub.graphistry.com",
personal_key_id=personal_key,
personal_key_secret=secret_key
)
logger.info("PyGraphistry initialized with personal key/secret for cloud GPU acceleration")
return True
elif api_key:
# Configure for cloud API with API key
graphistry.register(api=3, protocol="https", server="hub.graphistry.com", api_key=api_key)
logger.info("PyGraphistry initialized with API key for cloud GPU acceleration")
return True
elif username and password:
# Configure for cloud API with username/password
graphistry.register(api=3, protocol="https", server="hub.graphistry.com",
username=username, password=password)
logger.info("PyGraphistry initialized with username/password for cloud GPU acceleration")
return True
else:
# Configure for local mode
graphistry.register(api=3)
logger.info("PyGraphistry initialized in local CPU mode")
return True
except Exception as e:
logger.error(f"Failed to initialize PyGraphistry: {e}")
return False
class GraphPattern(str, Enum):
RANDOM = "random"
SCALE_FREE = "scale-free"
SMALL_WORLD = "small-world"
CLUSTERED = "clustered"
HIERARCHICAL = "hierarchical"
GRID = "grid"
class GraphData(BaseModel):
nodes: List[Dict[str, Any]]
links: List[Dict[str, Any]]
class GraphGenerationRequest(BaseModel):
num_nodes: int
pattern: GraphPattern = GraphPattern.SCALE_FREE
avg_degree: Optional[int] = 5
num_clusters: Optional[int] = 100
small_world_k: Optional[int] = 6
small_world_p: Optional[float] = 0.1
grid_dimensions: Optional[List[int]] = [100, 100]
seed: Optional[int] = None
class VisualizationRequest(BaseModel):
graph_data: GraphData
layout_type: Optional[str] = "force"
gpu_acceleration: Optional[bool] = True
clustering: Optional[bool] = False
node_size_attribute: Optional[str] = None
node_color_attribute: Optional[str] = None
edge_weight_attribute: Optional[str] = None
class GraphGenerationStatus(BaseModel):
task_id: str
status: str # "running", "completed", "failed"
progress: float
message: str
result: Optional[Dict[str, Any]] = None
error: Optional[str] = None
class LargeGraphGenerator:
"""Optimized graph generation using NetworkX and NumPy for performance"""
@staticmethod
def generate_random_graph(num_nodes: int, avg_degree: int = 5, seed: Optional[int] = None) -> GraphData:
"""Generate random graph using ErdősRényi model"""
if seed:
np.random.seed(seed)
# Calculate edge probability for desired average degree
p = avg_degree / (num_nodes - 1)
# Use NetworkX for efficient generation
G = nx.erdos_renyi_graph(num_nodes, p, seed=seed)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_scale_free_graph(num_nodes: int, m: int = 3, seed: Optional[int] = None) -> GraphData:
"""Generate scale-free graph using BarabásiAlbert model"""
G = nx.barabasi_albert_graph(num_nodes, m, seed=seed)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_small_world_graph(num_nodes: int, k: int = 6, p: float = 0.1, seed: Optional[int] = None) -> GraphData:
"""Generate small-world graph using Watts-Strogatz model"""
G = nx.watts_strogatz_graph(num_nodes, k, p, seed=seed)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_clustered_graph(num_nodes: int, num_clusters: int = 100, seed: Optional[int] = None) -> GraphData:
"""Generate clustered graph with intra and inter-cluster connections"""
if seed:
np.random.seed(seed)
cluster_size = num_nodes // num_clusters
G = nx.Graph()
# Add nodes with cluster information
for i in range(num_nodes):
cluster_id = i // cluster_size
G.add_node(i, cluster=cluster_id)
# Generate intra-cluster edges
intra_prob = 0.1
for cluster in range(num_clusters):
cluster_start = cluster * cluster_size
cluster_end = min(cluster_start + cluster_size, num_nodes)
cluster_nodes = list(range(cluster_start, cluster_end))
# Create subgraph for cluster
cluster_subgraph = nx.erdos_renyi_graph(len(cluster_nodes), intra_prob)
# Add edges to main graph with proper node mapping
for edge in cluster_subgraph.edges():
G.add_edge(cluster_nodes[edge[0]], cluster_nodes[edge[1]])
# Generate inter-cluster edges
inter_prob = 0.001
for i in range(num_nodes):
for j in range(i + 1, num_nodes):
if G.nodes[i].get('cluster') != G.nodes[j].get('cluster'):
if np.random.random() < inter_prob:
G.add_edge(i, j)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_hierarchical_graph(num_nodes: int, branching_factor: int = 3, seed: Optional[int] = None) -> GraphData:
"""Generate hierarchical (tree-like) graph"""
G = nx.random_tree(num_nodes, seed=seed)
# Add some cross-links to make it more interesting
if seed:
np.random.seed(seed)
# Add 10% additional edges for cross-connections
num_additional_edges = max(1, num_nodes // 10)
nodes = list(G.nodes())
for _ in range(num_additional_edges):
u, v = np.random.choice(nodes, 2, replace=False)
if not G.has_edge(u, v):
G.add_edge(u, v)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_grid_graph(dimensions: List[int], seed: Optional[int] = None) -> GraphData:
"""Generate 2D or 3D grid graph"""
if len(dimensions) == 2:
G = nx.grid_2d_graph(dimensions[0], dimensions[1])
elif len(dimensions) == 3:
G = nx.grid_graph(dimensions)
else:
raise ValueError("Grid dimensions must be 2D or 3D")
# Convert coordinate tuples to integer node IDs
mapping = {node: i for i, node in enumerate(G.nodes())}
G = nx.relabel_nodes(G, mapping)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def _networkx_to_graphdata(G: nx.Graph) -> GraphData:
"""Convert NetworkX graph to GraphData format"""
nodes = []
links = []
# Convert nodes
for node_id in G.nodes():
node_data = G.nodes[node_id]
node = {
"id": f"n{node_id}",
"name": f"Node {node_id}",
"val": np.random.randint(1, 11),
"degree": G.degree(node_id)
}
# Add cluster information if available
if 'cluster' in node_data:
node['group'] = f"cluster_{node_data['cluster']}"
else:
node['group'] = f"group_{node_id % 10}"
nodes.append(node)
# Convert edges
for edge in G.edges():
link = {
"source": f"n{edge[0]}",
"target": f"n{edge[1]}",
"name": f"link_{edge[0]}_{edge[1]}",
"weight": np.random.uniform(0.1, 5.0)
}
links.append(link)
return GraphData(nodes=nodes, links=links)
class PyGraphistryService:
def __init__(self):
self.initialized = init_graphistry()
self.generation_tasks = {} # Store background tasks
self.executor = ThreadPoolExecutor(max_workers=4)
async def generate_graph_async(self, request: GraphGenerationRequest, task_id: str):
"""Generate graph asynchronously"""
try:
self.generation_tasks[task_id] = GraphGenerationStatus(
task_id=task_id,
status="running",
progress=0.0,
message="Starting graph generation..."
)
start_time = time.time()
# Update progress
self.generation_tasks[task_id].progress = 10.0
self.generation_tasks[task_id].message = f"Generating {request.pattern.value} graph with {request.num_nodes} nodes..."
# Generate graph based on pattern
if request.pattern == GraphPattern.RANDOM:
graph_data = LargeGraphGenerator.generate_random_graph(
request.num_nodes, request.avg_degree, request.seed
)
elif request.pattern == GraphPattern.SCALE_FREE:
m = min(request.avg_degree, request.num_nodes - 1) if request.avg_degree else 3
graph_data = LargeGraphGenerator.generate_scale_free_graph(
request.num_nodes, m, request.seed
)
elif request.pattern == GraphPattern.SMALL_WORLD:
graph_data = LargeGraphGenerator.generate_small_world_graph(
request.num_nodes,
request.small_world_k or 6,
request.small_world_p or 0.1,
request.seed
)
elif request.pattern == GraphPattern.CLUSTERED:
graph_data = LargeGraphGenerator.generate_clustered_graph(
request.num_nodes, request.num_clusters or 100, request.seed
)
elif request.pattern == GraphPattern.HIERARCHICAL:
graph_data = LargeGraphGenerator.generate_hierarchical_graph(
request.num_nodes, seed=request.seed
)
elif request.pattern == GraphPattern.GRID:
# Calculate grid dimensions for given number of nodes
if request.grid_dimensions:
dimensions = request.grid_dimensions
else:
side_length = int(np.sqrt(request.num_nodes))
dimensions = [side_length, side_length]
graph_data = LargeGraphGenerator.generate_grid_graph(dimensions, request.seed)
else:
raise ValueError(f"Unknown graph pattern: {request.pattern}")
# Update progress
self.generation_tasks[task_id].progress = 80.0
self.generation_tasks[task_id].message = "Computing graph statistics..."
# Calculate statistics
generation_time = time.time() - start_time
stats = {
"node_count": len(graph_data.nodes),
"edge_count": len(graph_data.links),
"generation_time": generation_time,
"density": len(graph_data.links) / (len(graph_data.nodes) * (len(graph_data.nodes) - 1) / 2) if len(graph_data.nodes) > 1 else 0,
"avg_degree": 2 * len(graph_data.links) / len(graph_data.nodes) if len(graph_data.nodes) > 0 else 0,
"pattern": request.pattern.value,
"parameters": request.model_dump()
}
# Complete task
self.generation_tasks[task_id].status = "completed"
self.generation_tasks[task_id].progress = 100.0
self.generation_tasks[task_id].message = f"Generated {stats['node_count']} nodes and {stats['edge_count']} edges in {generation_time:.2f}s"
self.generation_tasks[task_id].result = {
"graph_data": graph_data.model_dump(),
"stats": stats
}
logger.info(f"Graph generation completed for task {task_id}: {stats}")
except Exception as e:
logger.error(f"Graph generation failed for task {task_id}: {e}")
self.generation_tasks[task_id].status = "failed"
self.generation_tasks[task_id].error = str(e)
self.generation_tasks[task_id].message = f"Generation failed: {e}"
async def start_graph_generation(self, request: GraphGenerationRequest) -> str:
"""Start graph generation as background task"""
task_id = f"gen_{int(time.time() * 1000)}"
# Run generation in thread pool to avoid blocking
loop = asyncio.get_event_loop()
loop.run_in_executor(
self.executor,
lambda: asyncio.run(self.generate_graph_async(request, task_id))
)
return task_id
def get_generation_status(self, task_id: str) -> Optional[GraphGenerationStatus]:
"""Get status of graph generation task"""
return self.generation_tasks.get(task_id)
async def process_graph_data(self, request: VisualizationRequest) -> Dict[str, Any]:
"""Process graph data with PyGraphistry GPU acceleration"""
try:
if not self.initialized:
raise HTTPException(status_code=500, detail="PyGraphistry not initialized")
# Convert to pandas DataFrames for PyGraphistry
nodes_df = pd.DataFrame(request.graph_data.nodes)
edges_df = pd.DataFrame(request.graph_data.links)
# Ensure required columns exist
if 'id' not in nodes_df.columns:
nodes_df['id'] = nodes_df.index
if 'source' not in edges_df.columns or 'target' not in edges_df.columns:
raise HTTPException(status_code=400, detail="Links must have source and target columns")
logger.info(f"Processing graph with {len(nodes_df)} nodes and {len(edges_df)} edges")
# Create PyGraphistry graph object
try:
g = graphistry.edges(edges_df, 'source', 'target').nodes(nodes_df, 'id')
logger.info(f"Created PyGraphistry graph object")
except Exception as e:
logger.error(f"Failed to create PyGraphistry graph: {e}")
raise HTTPException(status_code=500, detail=f"Graph creation failed: {e}")
# Apply GPU-accelerated processing
if request.gpu_acceleration:
g = await self._apply_gpu_acceleration(g, request)
# Apply clustering if requested
if request.clustering:
g = await self._apply_clustering(g)
# Generate layout
g = await self._generate_layout(g, request.layout_type)
# Extract processed data
try:
processed_nodes = g._nodes.to_dict('records') if g._nodes is not None else nodes_df.to_dict('records')
processed_edges = g._edges.to_dict('records') if g._edges is not None else edges_df.to_dict('records')
logger.info(f"Extracted {len(processed_nodes)} nodes and {len(processed_edges)} edges")
except Exception as e:
logger.warning(f"Data extraction failed, using original data: {e}")
processed_nodes = nodes_df.to_dict('records')
processed_edges = edges_df.to_dict('records')
# Generate embedding URL for interactive visualization
embed_url = None
local_viz_data = None
try:
embed_url = g.plot(render=False)
logger.info(f"Generated PyGraphistry embed URL: {embed_url}")
except Exception as e:
logger.warning(f"Could not generate embed URL (likely running in local mode): {e}")
# Create local visualization data as fallback
try:
local_viz_data = self._create_local_viz_data(g, processed_nodes, processed_edges)
logger.info("Generated local visualization data as fallback")
except Exception as viz_e:
logger.warning(f"Could not generate local visualization data: {viz_e}")
return {
"processed_nodes": processed_nodes,
"processed_edges": processed_edges,
"embed_url": embed_url,
"local_viz_data": local_viz_data,
"stats": {
"node_count": len(processed_nodes),
"edge_count": len(processed_edges),
"gpu_accelerated": request.gpu_acceleration,
"clustered": request.clustering,
"layout_type": request.layout_type,
"has_embed_url": embed_url is not None,
"has_local_viz": local_viz_data is not None
},
"timestamp": datetime.now().isoformat()
}
except Exception as e:
logger.error(f"Error processing graph data: {e}")
raise HTTPException(status_code=500, detail=str(e))
async def _apply_gpu_acceleration(self, g, request: VisualizationRequest):
"""Apply GPU acceleration using PyGraphistry's vector processing"""
try:
if not request.gpu_acceleration:
logger.info("GPU acceleration disabled by request")
return g
logger.info("=== GPU ACCELERATION ATTEMPT ===")
logger.info(f"PyGraphistry object type: {type(g)}")
logger.info(f"Available methods: {[method for method in dir(g) if not method.startswith('_')]}")
# Check what GPU methods are actually available
has_compute_igraph = hasattr(g, 'compute_igraph')
has_umap = hasattr(g, 'umap')
logger.info(f"Has compute_igraph: {has_compute_igraph}")
logger.info(f"Has UMAP: {has_umap}")
gpu_operations_successful = 0
total_gpu_operations = 0
# Compute centrality measures if available
total_gpu_operations += 1
try:
if has_compute_igraph and len(g._nodes) < 50000: # Limit for performance
logger.info("Attempting PageRank computation...")
g = g.compute_igraph('pagerank', out_col='pagerank')
logger.info("✓ SUCCESS: Computed PageRank centrality with GPU")
gpu_operations_successful += 1
else:
reason = "too many nodes" if len(g._nodes) >= 50000 else "compute_igraph not available"
logger.warning(f"✗ SKIPPED: PageRank computation ({reason})")
except Exception as e:
logger.warning(f"✗ FAILED: PageRank computation failed: {e}")
# Apply UMAP for node positioning if available and beneficial
total_gpu_operations += 1
try:
if has_umap and len(g._nodes) > 100 and len(g._nodes) < 10000:
logger.info("Attempting UMAP for node positioning...")
g = g.umap()
logger.info("✓ SUCCESS: Applied UMAP for node positioning")
gpu_operations_successful += 1
else:
reason = ("UMAP not available" if not has_umap else
"too few nodes" if len(g._nodes) <= 100 else "too many nodes")
logger.warning(f"✗ SKIPPED: UMAP processing ({reason})")
except Exception as e:
logger.warning(f"✗ FAILED: UMAP processing failed: {e}")
logger.info(f"=== GPU ACCELERATION SUMMARY ===")
logger.info(f"GPU operations successful: {gpu_operations_successful}/{total_gpu_operations}")
logger.info(f"GPU utilization: {(gpu_operations_successful/total_gpu_operations)*100:.1f}%")
return g
except Exception as e:
logger.warning(f"GPU acceleration failed completely, falling back to CPU: {e}")
return g
async def _apply_clustering(self, g):
"""Apply GPU-accelerated clustering"""
try:
logger.info("=== CLUSTERING ATTEMPT ===")
# Use PyGraphistry's built-in clustering if available
if hasattr(g, 'compute_igraph') and len(g._nodes) < 20000: # Limit for performance
logger.info("Attempting Leiden community detection...")
try:
g = g.compute_igraph('community_leiden', out_col='cluster')
logger.info("✓ SUCCESS: Applied Leiden community detection")
return g
except Exception as e:
logger.warning(f"✗ FAILED: Leiden clustering failed: {e}")
logger.info("Attempting Louvain community detection as fallback...")
try:
g = g.compute_igraph('community_louvain', out_col='cluster')
logger.info("✓ SUCCESS: Applied Louvain community detection")
return g
except Exception as e2:
logger.warning(f"✗ FAILED: Louvain clustering also failed: {e2}")
else:
reason = "too many nodes" if len(g._nodes) >= 20000 else "compute_igraph not available"
logger.warning(f"✗ SKIPPED: Clustering ({reason})")
logger.info("=== CLUSTERING SUMMARY: No clustering applied ===")
return g
except Exception as e:
logger.warning(f"Clustering failed completely: {e}")
return g
async def _generate_layout(self, g, layout_type: str = "force"):
"""Generate layout using PyGraphistry's algorithms"""
try:
logger.info(f"Generating {layout_type} layout")
# Only apply layout computation for reasonable graph sizes
if len(g._nodes) > 50000:
logger.info("Skipping layout computation for very large graph")
return g
if hasattr(g, 'compute_igraph'):
try:
if layout_type == "force":
g = g.compute_igraph('layout_fruchterman_reingold', out_cols=['x', 'y'])
logger.info("Applied Fruchterman-Reingold force layout")
elif layout_type == "circular":
g = g.compute_igraph('layout_circle', out_cols=['x', 'y'])
logger.info("Applied circular layout")
elif layout_type == "hierarchical":
g = g.compute_igraph('layout_sugiyama', out_cols=['x', 'y'])
logger.info("Applied hierarchical layout")
else:
# Default to force-directed
g = g.compute_igraph('layout_fruchterman_reingold', out_cols=['x', 'y'])
logger.info("Applied default force layout")
except Exception as e:
logger.warning(f"Layout computation failed: {e}")
else:
logger.info("Layout computation not available, using default positioning")
return g
except Exception as e:
logger.warning(f"Layout generation failed: {e}")
return g
def _create_local_viz_data(self, g, processed_nodes: List[Dict], processed_edges: List[Dict]) -> Dict[str, Any]:
"""Create local visualization data when embed URL cannot be generated"""
try:
# Extract layout positions if available
positions = {}
if g._nodes is not None and 'x' in g._nodes.columns and 'y' in g._nodes.columns:
for _, row in g._nodes.iterrows():
node_id = row.get('id', row.name)
positions[str(node_id)] = {
'x': float(row['x']) if pd.notna(row['x']) else 0,
'y': float(row['y']) if pd.notna(row['y']) else 0
}
# Add cluster information if available
clusters = {}
if g._nodes is not None and 'cluster' in g._nodes.columns:
for _, row in g._nodes.iterrows():
node_id = row.get('id', row.name)
if pd.notna(row['cluster']):
clusters[str(node_id)] = int(row['cluster'])
# Create enhanced nodes with layout and cluster info
enhanced_nodes = []
for node in processed_nodes:
enhanced_node = node.copy()
node_id = str(node.get('id', ''))
if node_id in positions:
enhanced_node.update(positions[node_id])
if node_id in clusters:
enhanced_node['cluster'] = clusters[node_id]
enhanced_nodes.append(enhanced_node)
return {
"nodes": enhanced_nodes,
"edges": processed_edges,
"positions": positions,
"clusters": clusters,
"layout_computed": len(positions) > 0,
"clusters_computed": len(clusters) > 0
}
except Exception as e:
logger.error(f"Failed to create local visualization data: {e}")
return {
"nodes": processed_nodes,
"edges": processed_edges,
"positions": {},
"clusters": {},
"layout_computed": False,
"clusters_computed": False
}
async def get_graph_stats(self, graph_data: GraphData) -> Dict[str, Any]:
"""Get GPU-accelerated graph statistics"""
try:
nodes_df = pd.DataFrame(graph_data.nodes)
edges_df = pd.DataFrame(graph_data.links)
g = graphistry.edges(edges_df, 'source', 'target').nodes(nodes_df, 'id')
# Compute various graph metrics using GPU acceleration
stats = {
"node_count": len(nodes_df),
"edge_count": len(edges_df),
"density": len(edges_df) / (len(nodes_df) * (len(nodes_df) - 1)) if len(nodes_df) > 1 else 0,
"timestamp": datetime.now().isoformat()
}
# Add centrality measures if possible
try:
if len(nodes_df) < 10000 and hasattr(g, 'compute_igraph'): # Only for reasonably sized graphs
g_with_metrics = g.compute_igraph('pagerank', out_col='pagerank')
if g_with_metrics._nodes is not None and 'pagerank' in g_with_metrics._nodes.columns:
pagerank_data = g_with_metrics._nodes['pagerank'].to_list()
stats.update({
"avg_pagerank": float(np.mean(pagerank_data)),
"max_pagerank": float(np.max(pagerank_data))
})
logger.info("Computed PageRank statistics")
except Exception as e:
logger.warning(f"Could not compute centrality measures: {e}")
return stats
except Exception as e:
logger.error(f"Error computing graph stats: {e}")
raise HTTPException(status_code=500, detail=str(e))
# FastAPI app
app = FastAPI(title="PyGraphistry GPU Visualization Service", version="1.0.0")
service = PyGraphistryService()
@app.post("/api/generate")
async def generate_graph(request: GraphGenerationRequest):
"""Start graph generation as background task"""
if request.num_nodes > 1000000:
raise HTTPException(status_code=400, detail="Maximum 1 million nodes allowed")
task_id = await service.start_graph_generation(request)
return {"task_id": task_id, "status": "started"}
@app.get("/api/generate/{task_id}")
async def get_generation_status(task_id: str):
"""Get status of graph generation task"""
status = service.get_generation_status(task_id)
if not status:
raise HTTPException(status_code=404, detail="Task not found")
return status
@app.post("/api/visualize")
async def visualize_graph(request: VisualizationRequest):
"""Process graph data with PyGraphistry GPU acceleration"""
return await service.process_graph_data(request)
@app.post("/api/stats")
async def get_graph_statistics(graph_data: GraphData):
"""Get GPU-accelerated graph statistics"""
return await service.get_graph_stats(graph_data)
@app.get("/api/health")
async def health_check():
"""Health check endpoint"""
return {
"status": "healthy",
"pygraphistry_initialized": service.initialized,
"timestamp": datetime.now().isoformat()
}
@app.get("/api/patterns")
async def get_available_patterns():
"""Get list of available graph generation patterns"""
return {
"patterns": [
{
"name": pattern.value,
"description": {
GraphPattern.RANDOM: "Random graph using ErdősRényi model",
GraphPattern.SCALE_FREE: "Scale-free graph using BarabásiAlbert model",
GraphPattern.SMALL_WORLD: "Small-world graph using Watts-Strogatz model",
GraphPattern.CLUSTERED: "Clustered graph with community structure",
GraphPattern.HIERARCHICAL: "Hierarchical tree-like graph with cross-links",
GraphPattern.GRID: "2D or 3D grid graph"
}[pattern]
} for pattern in GraphPattern
]
}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8080)

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
""" """
Remote GPU Rendering Service Remote GPU Rendering Service

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
""" """
Remote WebGPU Clustering Service - CuPy Version with Semantic Clustering Remote WebGPU Clustering Service - CuPy Version with Semantic Clustering

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
""" """
Remote WebGPU Clustering Service - CuPy Version Remote WebGPU Clustering Service - CuPy Version

View File

@ -1,3 +1,4 @@
graphistry>=0.32.0
pandas>=2.0.0 pandas>=2.0.0
numpy>=1.24.0 numpy>=1.24.0
fastapi>=0.104.0 fastapi>=0.104.0
@ -6,6 +7,7 @@ pydantic>=2.0.0
networkx>=3.0 # For efficient graph generation algorithms networkx>=3.0 # For efficient graph generation algorithms
# cudf, cuml, cugraph are already included in PyG container # cudf, cuml, cugraph are already included in PyG container
# cupy>=12.0.0 # Already included in PyG container # cupy>=12.0.0 # Already included in PyG container
igraph>=0.10.0 # For additional graph algorithms
scikit-learn>=1.3.0 # For additional ML features scikit-learn>=1.3.0 # For additional ML features
requests>=2.31.0 requests>=2.31.0
aiofiles>=23.0.0 aiofiles>=23.0.0

View File

@ -1,3 +1,19 @@
#
# 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.
#
""" """
Semantic Clustering Service for Knowledge Graphs Semantic Clustering Service for Knowledge Graphs
Groups nodes by semantic similarity of names, types, and content rather than just spatial coordinates Groups nodes by semantic similarity of names, types, and content rather than just spatial coordinates

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
""" """
Simple WebGPU clustering test service Simple WebGPU clustering test service
Minimal implementation to test basic functionality Minimal implementation to test basic functionality

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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.
#
# Start Remote GPU Rendering Services # Start Remote GPU Rendering Services
# This script starts the custom remote GPU rendering service as an alternative to PyGraphistry cloud # This script starts the custom remote GPU rendering service as an alternative to PyGraphistry cloud

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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.
#
# Stop Remote GPU Rendering Services # Stop Remote GPU Rendering Services

View File

@ -1,8 +1,25 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
""" """
Unified GPU Graph Visualization Service Unified GPU Graph Visualization Service
Provides local GPU processing with cuGraph and CPU fallback with NetworkX. Combines PyGraphistry cloud processing and local GPU processing with cuGraph
into a single FastAPI service for maximum flexibility.
""" """
import os import os
@ -22,6 +39,9 @@ from concurrent.futures import ThreadPoolExecutor
import networkx as nx import networkx as nx
from enum import Enum from enum import Enum
# PyGraphistry imports
import graphistry
# GPU-accelerated imports (available in NVIDIA PyG container) # GPU-accelerated imports (available in NVIDIA PyG container)
try: try:
import cudf import cudf
@ -48,6 +68,7 @@ logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ProcessingMode(str, Enum): class ProcessingMode(str, Enum):
PYGRAPHISTRY_CLOUD = "pygraphistry_cloud"
LOCAL_GPU = "local_gpu" LOCAL_GPU = "local_gpu"
LOCAL_CPU = "local_cpu" LOCAL_CPU = "local_cpu"
@ -75,7 +96,12 @@ class GraphGenerationRequest(BaseModel):
class UnifiedVisualizationRequest(BaseModel): class UnifiedVisualizationRequest(BaseModel):
graph_data: GraphData graph_data: GraphData
processing_mode: ProcessingMode = ProcessingMode.LOCAL_GPU processing_mode: ProcessingMode = ProcessingMode.PYGRAPHISTRY_CLOUD
# PyGraphistry Cloud options
layout_type: Optional[str] = "force"
gpu_acceleration: Optional[bool] = True
clustering: Optional[bool] = False
# Local GPU options # Local GPU options
layout_algorithm: Optional[str] = "force_atlas2" layout_algorithm: Optional[str] = "force_atlas2"
@ -90,144 +116,8 @@ class GraphGenerationStatus(BaseModel):
result: Optional[Dict[str, Any]] = None result: Optional[Dict[str, Any]] = None
error: Optional[str] = None error: Optional[str] = None
class LargeGraphGenerator: # Import graph generation classes (keeping existing code)
"""Optimized graph generation using NetworkX and NumPy for performance""" from pygraphistry_service import LargeGraphGenerator, init_graphistry
@staticmethod
def generate_random_graph(num_nodes: int, avg_degree: int = 5, seed: Optional[int] = None) -> GraphData:
"""Generate random graph using ErdősRényi model"""
if seed:
np.random.seed(seed)
# Calculate edge probability for desired average degree
p = avg_degree / (num_nodes - 1)
# Use NetworkX for efficient generation
G = nx.erdos_renyi_graph(num_nodes, p, seed=seed)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_scale_free_graph(num_nodes: int, m: int = 3, seed: Optional[int] = None) -> GraphData:
"""Generate scale-free graph using BarabásiAlbert model"""
G = nx.barabasi_albert_graph(num_nodes, m, seed=seed)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_small_world_graph(num_nodes: int, k: int = 6, p: float = 0.1, seed: Optional[int] = None) -> GraphData:
"""Generate small-world graph using Watts-Strogatz model"""
G = nx.watts_strogatz_graph(num_nodes, k, p, seed=seed)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_clustered_graph(num_nodes: int, num_clusters: int = 100, seed: Optional[int] = None) -> GraphData:
"""Generate clustered graph with intra and inter-cluster connections"""
if seed:
np.random.seed(seed)
cluster_size = num_nodes // num_clusters
G = nx.Graph()
# Add nodes with cluster information
for i in range(num_nodes):
cluster_id = i // cluster_size
G.add_node(i, cluster=cluster_id)
# Generate intra-cluster edges
intra_prob = 0.1
for cluster in range(num_clusters):
cluster_start = cluster * cluster_size
cluster_end = min(cluster_start + cluster_size, num_nodes)
cluster_nodes = list(range(cluster_start, cluster_end))
# Create subgraph for cluster
cluster_subgraph = nx.erdos_renyi_graph(len(cluster_nodes), intra_prob)
# Add edges to main graph with proper node mapping
for edge in cluster_subgraph.edges():
G.add_edge(cluster_nodes[edge[0]], cluster_nodes[edge[1]])
# Generate inter-cluster edges
inter_prob = 0.001
for i in range(num_nodes):
for j in range(i + 1, num_nodes):
if G.nodes[i].get('cluster') != G.nodes[j].get('cluster'):
if np.random.random() < inter_prob:
G.add_edge(i, j)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_hierarchical_graph(num_nodes: int, branching_factor: int = 3, seed: Optional[int] = None) -> GraphData:
"""Generate hierarchical (tree-like) graph"""
G = nx.random_tree(num_nodes, seed=seed)
# Add some cross-links to make it more interesting
if seed:
np.random.seed(seed)
# Add 10% additional edges for cross-connections
num_additional_edges = max(1, num_nodes // 10)
nodes = list(G.nodes())
for _ in range(num_additional_edges):
u, v = np.random.choice(nodes, 2, replace=False)
if not G.has_edge(u, v):
G.add_edge(u, v)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def generate_grid_graph(dimensions: List[int], seed: Optional[int] = None) -> GraphData:
"""Generate 2D or 3D grid graph"""
if len(dimensions) == 2:
G = nx.grid_2d_graph(dimensions[0], dimensions[1])
elif len(dimensions) == 3:
G = nx.grid_graph(dimensions)
else:
raise ValueError("Grid dimensions must be 2D or 3D")
# Convert coordinate tuples to integer node IDs
mapping = {node: i for i, node in enumerate(G.nodes())}
G = nx.relabel_nodes(G, mapping)
return LargeGraphGenerator._networkx_to_graphdata(G)
@staticmethod
def _networkx_to_graphdata(G: nx.Graph) -> GraphData:
"""Convert NetworkX graph to GraphData format"""
nodes = []
links = []
# Convert nodes
for node_id in G.nodes():
node_data = G.nodes[node_id]
node = {
"id": f"n{node_id}",
"name": f"Node {node_id}",
"val": np.random.randint(1, 11),
"degree": G.degree(node_id)
}
# Add cluster information if available
if 'cluster' in node_data:
node['group'] = f"cluster_{node_data['cluster']}"
else:
node['group'] = f"group_{node_id % 10}"
nodes.append(node)
# Convert edges
for edge in G.edges():
link = {
"source": f"n{edge[0]}",
"target": f"n{edge[1]}",
"name": f"link_{edge[0]}_{edge[1]}",
"weight": np.random.uniform(0.1, 5.0)
}
links.append(link)
return GraphData(nodes=nodes, links=links)
class LocalGPUProcessor: class LocalGPUProcessor:
"""GPU-accelerated graph processing using cuGraph""" """GPU-accelerated graph processing using cuGraph"""
@ -333,10 +223,109 @@ class LocalGPUProcessor:
logger.error(f"GPU centrality computation failed: {e}") logger.error(f"GPU centrality computation failed: {e}")
return {} return {}
class UnifiedGPUService: class PyGraphistryProcessor:
"""Unified service offering local GPU and CPU processing""" """PyGraphistry cloud processing (existing functionality)"""
def __init__(self): def __init__(self):
self.initialized = init_graphistry()
async def process_graph_data(self, request: UnifiedVisualizationRequest) -> Dict[str, Any]:
"""Process graph data with PyGraphistry GPU acceleration"""
try:
if not self.initialized:
raise HTTPException(status_code=500, detail="PyGraphistry not initialized")
# Convert to pandas DataFrames for PyGraphistry
nodes_df = pd.DataFrame(request.graph_data.nodes)
edges_df = pd.DataFrame(request.graph_data.links)
# Ensure required columns exist
if 'id' not in nodes_df.columns:
nodes_df['id'] = nodes_df.index
if 'source' not in edges_df.columns or 'target' not in edges_df.columns:
raise HTTPException(status_code=400, detail="Links must have source and target columns")
logger.info(f"Processing graph with {len(nodes_df)} nodes and {len(edges_df)} edges")
# Create PyGraphistry graph object
g = graphistry.edges(edges_df, 'source', 'target').nodes(nodes_df, 'id')
# Apply GPU-accelerated processing
if request.gpu_acceleration:
g = await self._apply_gpu_acceleration(g, request)
# Apply clustering if requested
if request.clustering:
g = await self._apply_clustering(g)
# Generate layout
g = await self._generate_layout(g, request.layout_type)
# Extract processed data
processed_nodes = g._nodes.to_dict('records') if g._nodes is not None else nodes_df.to_dict('records')
processed_edges = g._edges.to_dict('records') if g._edges is not None else edges_df.to_dict('records')
# Generate embedding URL for interactive visualization
embed_url = None
local_viz_data = None
try:
embed_url = g.plot(render=False)
logger.info(f"Generated PyGraphistry embed URL: {embed_url}")
except Exception as e:
logger.warning(f"Could not generate embed URL (likely running in local mode): {e}")
# Create local visualization data as fallback
try:
local_viz_data = self._create_local_viz_data(g, processed_nodes, processed_edges)
logger.info("Generated local visualization data as fallback")
except Exception as viz_e:
logger.warning(f"Could not generate local visualization data: {viz_e}")
return {
"processed_nodes": processed_nodes,
"processed_edges": processed_edges,
"embed_url": embed_url,
"local_viz_data": local_viz_data,
"processing_mode": ProcessingMode.PYGRAPHISTRY_CLOUD,
"stats": {
"node_count": len(processed_nodes),
"edge_count": len(processed_edges),
"gpu_accelerated": request.gpu_acceleration,
"clustered": request.clustering,
"layout_type": request.layout_type,
"has_embed_url": embed_url is not None,
"has_local_viz": local_viz_data is not None
},
"timestamp": datetime.now().isoformat()
}
except Exception as e:
logger.error(f"Error processing graph data: {e}")
raise HTTPException(status_code=500, detail=str(e))
# ... (include other PyGraphistry methods from original service)
async def _apply_gpu_acceleration(self, g, request):
# Implementation from original service
pass
async def _apply_clustering(self, g):
# Implementation from original service
pass
async def _generate_layout(self, g, layout_type):
# Implementation from original service
pass
def _create_local_viz_data(self, g, processed_nodes, processed_edges):
# Implementation from original service
pass
class UnifiedGPUService:
"""Unified service offering both PyGraphistry cloud and local GPU processing"""
def __init__(self):
self.pygraphistry_processor = PyGraphistryProcessor()
self.local_gpu_processor = LocalGPUProcessor() self.local_gpu_processor = LocalGPUProcessor()
self.generation_tasks = {} self.generation_tasks = {}
self.executor = ThreadPoolExecutor(max_workers=4) self.executor = ThreadPoolExecutor(max_workers=4)
@ -345,8 +334,12 @@ class UnifiedGPUService:
async def process_graph(self, request: UnifiedVisualizationRequest) -> Dict[str, Any]: async def process_graph(self, request: UnifiedVisualizationRequest) -> Dict[str, Any]:
"""Process graph with selected processing mode""" """Process graph with selected processing mode"""
if request.processing_mode == ProcessingMode.LOCAL_GPU: if request.processing_mode == ProcessingMode.PYGRAPHISTRY_CLOUD:
return await self.pygraphistry_processor.process_graph_data(request)
elif request.processing_mode == ProcessingMode.LOCAL_GPU:
return await self._process_with_local_gpu(request) return await self._process_with_local_gpu(request)
else: # LOCAL_CPU else: # LOCAL_CPU
return await self._process_with_local_cpu(request) return await self._process_with_local_cpu(request)
@ -458,7 +451,7 @@ service = UnifiedGPUService()
@app.post("/api/visualize") @app.post("/api/visualize")
async def visualize_graph(request: UnifiedVisualizationRequest): async def visualize_graph(request: UnifiedVisualizationRequest):
"""Process graph with unified service (supports local GPU and CPU modes)""" """Process graph with unified service (supports all processing modes)"""
result = await service.process_graph(request) result = await service.process_graph(request)
# Broadcast to connected WebSocket clients # Broadcast to connected WebSocket clients
@ -485,13 +478,17 @@ async def get_capabilities():
"""Get GPU capabilities and available processing modes""" """Get GPU capabilities and available processing modes"""
return { return {
"processing_modes": { "processing_modes": {
"pygraphistry_cloud": {
"available": service.pygraphistry_processor.initialized,
"description": "PyGraphistry cloud GPU processing with interactive embeds"
},
"local_gpu": { "local_gpu": {
"available": HAS_RAPIDS, "available": HAS_RAPIDS,
"description": "Local GPU processing with cuGraph/RAPIDS" "description": "Local GPU processing with cuGraph/RAPIDS"
}, },
"local_cpu": { "local_cpu": {
"available": True, "available": True,
"description": "Local CPU fallback processing with NetworkX" "description": "Local CPU fallback processing"
} }
}, },
"has_rapids": HAS_RAPIDS, "has_rapids": HAS_RAPIDS,
@ -543,6 +540,7 @@ async def health_check():
"""Health check endpoint""" """Health check endpoint"""
return { return {
"status": "healthy", "status": "healthy",
"pygraphistry_initialized": service.pygraphistry_processor.initialized,
"local_gpu_available": HAS_RAPIDS, "local_gpu_available": HAS_RAPIDS,
"torch_geometric": HAS_TORCH_GEOMETRIC, "torch_geometric": HAS_TORCH_GEOMETRIC,
"timestamp": datetime.now().isoformat() "timestamp": datetime.now().isoformat()
@ -573,6 +571,7 @@ async def get_visualization_page():
<div> <div>
<label>Processing Mode:</label> <label>Processing Mode:</label>
<select id="processingMode"> <select id="processingMode">
<option value="pygraphistry_cloud">PyGraphistry Cloud</option>
<option value="local_gpu">Local GPU (cuGraph)</option> <option value="local_gpu">Local GPU (cuGraph)</option>
<option value="local_cpu">Local CPU</option> <option value="local_cpu">Local CPU</option>
</select> </select>
@ -757,8 +756,23 @@ def startup_diagnostics():
else: else:
print("⚠ PyTorch Geometric not available") print("⚠ PyTorch Geometric not available")
# Check PyGraphistry credentials
print("Checking PyGraphistry credentials...")
personal_key = os.getenv('GRAPHISTRY_PERSONAL_KEY')
secret_key = os.getenv('GRAPHISTRY_SECRET_KEY')
api_key = os.getenv('GRAPHISTRY_API_KEY')
if personal_key and secret_key:
print("✓ PyGraphistry personal key/secret found")
elif api_key:
print("✓ PyGraphistry API key found")
else:
print("⚠ No PyGraphistry credentials found - cloud mode will be limited")
print(" Set GRAPHISTRY_PERSONAL_KEY + GRAPHISTRY_SECRET_KEY for full cloud features")
print("") print("")
print("🎯 Available Processing Modes:") print("🎯 Available Processing Modes:")
print(" ☁️ PyGraphistry Cloud - Interactive GPU embeds (requires credentials)")
print(" 🚀 Local GPU (cuGraph) - Full local GPU processing") print(" 🚀 Local GPU (cuGraph) - Full local GPU processing")
print(" 💻 Local CPU - NetworkX fallback") print(" 💻 Local CPU - NetworkX fallback")
print("") print("")

View File

@ -1,3 +1,19 @@
#
# 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.
#
# WebGL-Enhanced Remote GPU Rendering Service # WebGL-Enhanced Remote GPU Rendering Service
# Using Three.js for GPU-accelerated visualization # Using Three.js for GPU-accelerated visualization

View File

@ -1,4 +1,4 @@
FROM ollama/ollama:latest FROM ollama/ollama:0.12.6
# Copy the entrypoint script # Copy the entrypoint script
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh

View File

@ -1,5 +1,21 @@
#!/bin/bash #!/bin/bash
# #
# 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.
#
#
# Clear buffer cache and restart Ollama to fix unified memory detection # Clear buffer cache and restart Ollama to fix unified memory detection
# This script addresses the issue where Ollama can't see full GPU memory # This script addresses the issue where Ollama can't see full GPU memory
# due to buffer cache not being reclaimable in unified memory systems # due to buffer cache not being reclaimable in unified memory systems

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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.
#
set -e set -e
# Start Ollama server in the background # Start Ollama server in the background

View File

@ -1,3 +1,19 @@
#
# 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.
#
from flask import Flask, request, jsonify from flask import Flask, request, jsonify
from sentence_transformers import SentenceTransformer from sentence_transformers import SentenceTransformer
import os import os

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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 latest stable vLLM release for better compute capability 12.1 support # Use latest stable vLLM release for better compute capability 12.1 support
# Clone the vLLM GitHub repo and use latest stable release. # Clone the vLLM GitHub repo and use latest stable release.

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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.
#
# Launch vLLM with NVIDIA Triton Inference Server optimized build # Launch vLLM with NVIDIA Triton Inference Server optimized build
# This should have proper support for compute capability 12.1 (DGX Spark) # This should have proper support for compute capability 12.1 (DGX Spark)

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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.
#
# vLLM Llama3 8B Benchmark Runner # vLLM Llama3 8B Benchmark Runner
# Uses NVIDIA vLLM container for optimal performance # Uses NVIDIA vLLM container for optimal performance

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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.
#
# Follow the official vLLM tutorial run_container.sh exactly # Follow the official vLLM tutorial run_container.sh exactly
docker run -e HF_TOKEN="$HF_TOKEN" -e HF_HOME="$HF_HOME" --ipc=host --gpus all --entrypoint "/bin/bash" --rm -it vllm/vllm-openai:deploy docker run -e HF_TOKEN="$HF_TOKEN" -e HF_HOME="$HF_HOME" --ipc=host --gpus all --entrypoint "/bin/bash" --rm -it vllm/vllm-openai:deploy

View File

@ -1,4 +1,20 @@
#!/bin/bash #!/bin/bash
#
# 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.
#
# vLLM startup script with NVFP4 quantization support for Llama 4 Scout # vLLM startup script with NVFP4 quantization support for Llama 4 Scout
# Optimized for NVIDIA Blackwell and Hopper architectures # Optimized for NVIDIA Blackwell and Hopper architectures

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
""" """
Download and process the MTEB raw_biorxiv dataset for txt2kg demo. Download and process the MTEB raw_biorxiv dataset for txt2kg demo.
Filter for genetics/genomics categories and create individual txt files. Filter for genetics/genomics categories and create individual txt files.

View File

@ -1,4 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# 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.
#
""" """
Download and process the marianna13/biorxiv dataset for txt2kg demo. Download and process the marianna13/biorxiv dataset for txt2kg demo.
Filter for Creative Commons licensed papers and create individual txt files. Filter for Creative Commons licensed papers and create individual txt files.

View File

@ -1 +1,17 @@
//
// 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.
//
console.log('This is mock data to avoid SSR issues') console.log('This is mock data to avoid SSR issues')

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import remoteBackend from '@/lib/remote-backend'; import remoteBackend from '@/lib/remote-backend';
import type { Triple } from '@/types/graph'; import type { Triple } from '@/types/graph';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
export async function GET() { export async function GET() {

View File

@ -1,9 +1,25 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { EmbeddingsService } from '@/lib/embeddings'; import { EmbeddingsService } from '@/lib/embeddings';
import { PineconeService } from '@/lib/pinecone'; import { QdrantService } from '@/lib/qdrant';
/** /**
* Generate embeddings for text chunks and store them in Pinecone * Generate embeddings for text chunks and store them in Qdrant
*/ */
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
@ -39,14 +55,14 @@ export async function POST(request: NextRequest) {
const embeddings = await embeddingsService.encode(chunks); const embeddings = await embeddingsService.encode(chunks);
console.log(`Generated ${embeddings.length} embeddings`); console.log(`Generated ${embeddings.length} embeddings`);
// Initialize PineconeService // Initialize QdrantService
const pineconeService = PineconeService.getInstance(); const pineconeService = QdrantService.getInstance();
// Check if Pinecone server is running // Check if Qdrant server is running
const isPineconeRunning = await pineconeService.isPineconeRunning(); const isPineconeRunning = await pineconeService.isQdrantRunning();
if (!isPineconeRunning) { if (!isPineconeRunning) {
return NextResponse.json( return NextResponse.json(
{ error: 'Pinecone server is not available. Please make sure it is running.' }, { error: 'Qdrant server is not available. Please make sure it is running.' },
{ status: 503 } { status: 503 }
); );
} }

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { RemoteBackendService } from '@/lib/remote-backend'; import { RemoteBackendService } from '@/lib/remote-backend';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { processDocument, TextProcessor } from '@/lib/text-processor'; import { processDocument, TextProcessor } from '@/lib/text-processor';
import { llmService } from '@/lib/llm-service'; import { llmService } from '@/lib/llm-service';
@ -29,7 +45,8 @@ export async function POST(req: NextRequest) {
ollamaModel, ollamaModel,
ollamaBaseUrl, ollamaBaseUrl,
vllmModel, vllmModel,
vllmBaseUrl vllmBaseUrl,
nvidiaModel
} = body; } = body;
if (!text || typeof text !== 'string') { if (!text || typeof text !== 'string') {
@ -136,7 +153,8 @@ export async function POST(req: NextRequest) {
ollamaModel: ollamaModel, ollamaModel: ollamaModel,
ollamaBaseUrl: ollamaBaseUrl, ollamaBaseUrl: ollamaBaseUrl,
vllmModel: vllmModel, vllmModel: vllmModel,
vllmBaseUrl: vllmBaseUrl vllmBaseUrl: vllmBaseUrl,
nvidiaModel: nvidiaModel
}); });
} }

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import queryLoggerService, { QueryLogEntry } from '@/lib/query-logger'; import queryLoggerService, { QueryLogEntry } from '@/lib/query-logger';
import fs from 'fs'; import fs from 'fs';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { type NextRequest, NextResponse } from "next/server" import { type NextRequest, NextResponse } from "next/server"
// Utility function to generate UUID with fallback // Utility function to generate UUID with fallback

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { getGraphDbService } from '@/lib/graph-db-util'; import { getGraphDbService } from '@/lib/graph-db-util';
import { getGraphDbType } from '../../settings/route'; import { getGraphDbType } from '../../settings/route';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { getGraphDbService } from '@/lib/graph-db-util'; import { getGraphDbService } from '@/lib/graph-db-util';
import { getGraphDbType } from '../../settings/route'; import { getGraphDbType } from '../../settings/route';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { getGraphDbService } from '@/lib/graph-db-util'; import { getGraphDbService } from '@/lib/graph-db-util';
import { getGraphDbType } from '../settings/route'; import { getGraphDbType } from '../settings/route';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { getGraphDbService } from '@/lib/graph-db-util'; import { getGraphDbService } from '@/lib/graph-db-util';
import { getGraphDbType } from '../../settings/route'; import { getGraphDbType } from '../../settings/route';
@ -160,6 +176,16 @@ export async function POST(req: NextRequest) {
// Store triples in the graph database // Store triples in the graph database
await graphDbService.importTriples(validTriples); await graphDbService.importTriples(validTriples);
// Mark document as processed if documentName is provided (only for ArangoDB)
if (graphDbType === 'arangodb' && documentName) {
try {
await (graphDbService as any).markDocumentAsProcessed(documentName, validTriples.length);
} catch (error) {
console.error('Error marking document as processed:', error);
// Don't fail the request if marking fails
}
}
// Return success response // Return success response
return NextResponse.json({ return NextResponse.json({
success: true, success: true,

View File

@ -0,0 +1,66 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server';
import backendService from '@/lib/backend-service';
import { getGraphDbType } from '../settings/route';
/**
* API endpoint for LLM-enhanced graph query
* This retrieves triples using graph search and generates an answer using LLM
* Makes traditional graph search comparable to RAG for fair benchmarking
* POST /api/graph-query-llm
*/
export async function POST(request: NextRequest) {
try {
const { query, topK = 5, useTraditional = true, llmModel, llmProvider } = await request.json();
if (!query) {
return NextResponse.json({ error: 'Query is required' }, { status: 400 });
}
// Initialize backend if needed with the selected graph DB type
if (!backendService.isInitialized) {
const graphDbType = getGraphDbType();
console.log(`Initializing backend with graph DB type: ${graphDbType}`);
await backendService.initialize(graphDbType);
}
console.log(`Graph query with LLM: "${query}", topK=${topK}, traditional=${useTraditional}, model=${llmModel || 'default'}, provider=${llmProvider || 'default'}`);
// Query the backend with LLM enhancement
const result = await backendService.queryWithLLM(query, topK, useTraditional, llmModel, llmProvider);
// DEBUG: Log first triple in API route to verify depth/pathLength
if (result.triples && result.triples.length > 0) {
console.log('API route - first triple:', JSON.stringify(result.triples[0], null, 2));
}
// Return results
return NextResponse.json({
query,
answer: result.answer,
triples: result.triples,
count: result.count,
message: `Retrieved ${result.count} triples and generated answer using ${useTraditional ? 'traditional' : 'vector'} graph search + LLM`
});
} catch (error) {
console.error('Error in graph query with LLM:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return NextResponse.json({ error: errorMessage }, { status: 500 });
}
}

View File

@ -1,8 +1,24 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import remoteBackendInstance from '@/lib/remote-backend'; import remoteBackendInstance from '@/lib/remote-backend';
import { Neo4jService } from '@/lib/neo4j'; import { getGraphDbService } from '@/lib/graph-db-util';
import neo4jService from '@/lib/neo4j'; import { getGraphDbType } from '../settings/route';
import { PineconeService } from '@/lib/pinecone'; import { QdrantService } from '@/lib/qdrant';
import RAGService from '@/lib/rag'; import RAGService from '@/lib/rag';
import queryLoggerService, { QueryLogSummary } from '@/lib/query-logger'; import queryLoggerService, { QueryLogSummary } from '@/lib/query-logger';
@ -11,16 +27,31 @@ import queryLoggerService, { QueryLogSummary } from '@/lib/query-logger';
*/ */
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
try { try {
// Initialize services // Initialize services with the correct graph database type
const neo4j = Neo4jService.getInstance(); const graphDbType = getGraphDbType();
const pineconeService = PineconeService.getInstance(); const graphDbService = getGraphDbService(graphDbType);
const pineconeService = QdrantService.getInstance();
if (!neo4j.isInitialized()) { // Initialize graph database if needed
neo4j.initialize(); if (!graphDbService.isInitialized()) {
if (graphDbType === 'arangodb') {
await graphDbService.initialize(
process.env.ARANGODB_URL,
process.env.ARANGODB_DB,
process.env.ARANGODB_USER,
process.env.ARANGODB_PASSWORD
);
} else if (graphDbType === 'neo4j') {
graphDbService.initialize(
process.env.NEO4J_URI,
process.env.NEO4J_USER || process.env.NEO4J_USERNAME,
process.env.NEO4J_PASSWORD
);
}
} }
// Get graph stats from Neo4j // Get graph stats from the active graph database
const graphData = await neo4j.getGraphData(); const graphData = await graphDbService.getGraphData();
// Get unique entities (nodes) // Get unique entities (nodes)
const uniqueEntities = new Set<string>(); const uniqueEntities = new Set<string>();
@ -56,6 +87,7 @@ export async function GET(request: NextRequest) {
let f1Score = 0; let f1Score = 0;
let avgQueryTime = vectorStats.avgQueryTime || 0; let avgQueryTime = vectorStats.avgQueryTime || 0;
let avgRelevance = 0; let avgRelevance = 0;
let queryTimesByMode: Record<string, number> = {};
// Get query logs from file-based logger instead of Neo4j // Get query logs from file-based logger instead of Neo4j
try { try {
@ -91,6 +123,23 @@ export async function GET(request: NextRequest) {
avgRelevance = logsWithRelevance.reduce((sum, log) => sum + (log.metrics.avgRelevanceScore || 0), 0) / logsWithRelevance.length; avgRelevance = logsWithRelevance.reduce((sum, log) => sum + (log.metrics.avgRelevanceScore || 0), 0) / logsWithRelevance.length;
} }
} }
// Calculate per-mode query times
const logsByMode = queryLogs.reduce((acc, log) => {
const mode = log.queryMode || 'traditional';
if (!acc[mode]) acc[mode] = [];
acc[mode].push(log);
return acc;
}, {} as Record<string, typeof queryLogs>);
Object.entries(logsByMode).forEach(([mode, logs]) => {
const logsWithTime = logs.filter(log => log.metrics.avgExecutionTimeMs > 0);
if (logsWithTime.length > 0) {
queryTimesByMode[mode] = logsWithTime.reduce((sum, log) =>
sum + log.metrics.avgExecutionTimeMs, 0
) / logsWithTime.length;
}
});
} catch (error) { } catch (error) {
console.warn('Error getting query logs from file:', error); console.warn('Error getting query logs from file:', error);
// Keep values at 0 instead of using defaults // Keep values at 0 instead of using defaults
@ -117,6 +166,7 @@ export async function GET(request: NextRequest) {
recall, recall,
f1Score, f1Score,
topQueries, topQueries,
queryTimesByMode: queryTimesByMode || {},
// Add metadata about query logs // Add metadata about query logs
queryLogStats: { queryLogStats: {
totalQueryLogs: queryLogs.length, totalQueryLogs: queryLogs.length,

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
/** /**

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { Neo4jService } from '@/lib/neo4j'; import { Neo4jService } from '@/lib/neo4j';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
/** /**

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { llmService, LLMMessage } from '@/lib/llm-service'; import { llmService, LLMMessage } from '@/lib/llm-service';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { llmService } from '@/lib/llm-service'; import { llmService } from '@/lib/llm-service';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
/** /**

View File

@ -1,5 +1,21 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { PineconeService } from '@/lib/pinecone'; import { QdrantService } from '@/lib/qdrant';
/** /**
* Clear all data from the Pinecone vector database * Clear all data from the Pinecone vector database
@ -7,7 +23,7 @@ import { PineconeService } from '@/lib/pinecone';
*/ */
export async function POST() { export async function POST() {
// Get the Pinecone service instance // Get the Pinecone service instance
const pineconeService = PineconeService.getInstance(); const pineconeService = QdrantService.getInstance();
// Clear all vectors from the database // Clear all vectors from the database
const deleteSuccess = await pineconeService.deleteAllEntities(); const deleteSuccess = await pineconeService.deleteAllEntities();

View File

@ -1,5 +1,21 @@
//
// 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.
//
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { PineconeService } from '@/lib/pinecone'; import { QdrantService } from '@/lib/qdrant';
/** /**
* Create Pinecone index API endpoint * Create Pinecone index API endpoint
@ -8,7 +24,7 @@ import { PineconeService } from '@/lib/pinecone';
export async function POST() { export async function POST() {
try { try {
// Get the Pinecone service instance // Get the Pinecone service instance
const pineconeService = PineconeService.getInstance(); const pineconeService = QdrantService.getInstance();
// Force re-initialization to create the index // Force re-initialization to create the index
(pineconeService as any).initialized = false; (pineconeService as any).initialized = false;

View File

@ -1,5 +1,21 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { PineconeService } from '@/lib/pinecone'; import { QdrantService } from '@/lib/qdrant';
/** /**
* Get Pinecone vector database stats * Get Pinecone vector database stats
@ -7,7 +23,7 @@ import { PineconeService } from '@/lib/pinecone';
export async function GET() { export async function GET() {
try { try {
// Initialize Pinecone service // Initialize Pinecone service
const pineconeService = PineconeService.getInstance(); const pineconeService = QdrantService.getInstance();
// We can now directly call getStats() which handles initialization and error recovery // We can now directly call getStats() which handles initialization and error recovery
const stats = await pineconeService.getStats(); const stats = await pineconeService.getStats();

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { RemoteBackendService } from '@/lib/remote-backend'; import { RemoteBackendService } from '@/lib/remote-backend';
import { EmbeddingsService } from '@/lib/embeddings'; import { EmbeddingsService } from '@/lib/embeddings';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080' const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080'

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080' const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080'

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080' const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080'

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080' const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080'

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080' const PYGRAPHISTRY_SERVICE_URL = process.env.PYGRAPHISTRY_SERVICE_URL || 'http://localhost:8080'

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import neo4jService from '@/lib/neo4j'; import neo4jService from '@/lib/neo4j';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import queryLoggerService from '@/lib/query-logger'; import queryLoggerService from '@/lib/query-logger';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import neo4jService from '@/lib/neo4j'; import neo4jService from '@/lib/neo4j';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import backendService from '@/lib/backend-service'; import backendService from '@/lib/backend-service';
import type { Triple } from '@/types/graph'; import type { Triple } from '@/types/graph';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import RAGService from '@/lib/rag'; import RAGService from '@/lib/rag';
@ -20,17 +36,20 @@ export async function POST(req: NextRequest) {
const ragService = RAGService; const ragService = RAGService;
await ragService.initialize(); await ragService.initialize();
console.log(`Processing RAG query: "${query}" with topK=${topK}`); console.log(`Processing Pure RAG query: "${query}" with topK=${topK}`);
// Retrieve documents and generate answer // Retrieve documents and generate answer
const answer = await ragService.retrievalQA(query, topK); const result = await ragService.retrievalQA(query, topK);
// Check if this is a fallback response // Check if this is a fallback response
const isGeneralKnowledgeFallback = answer.startsWith('[Note: No specific information was found'); const isGeneralKnowledgeFallback = result.answer.startsWith('[Note: No specific information was found');
console.log(`✅ Pure RAG query completed. Retrieved ${result.documentCount} document chunks`);
// Return the results // Return the results
return NextResponse.json({ return NextResponse.json({
answer, answer: result.answer,
documentCount: result.documentCount,
usedFallback: isGeneralKnowledgeFallback, usedFallback: isGeneralKnowledgeFallback,
success: true success: true
}); });

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
// Proxy route specifically for WebRTC streaming frames // Proxy route specifically for WebRTC streaming frames

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
// Proxy route for remote WebGPU clustering service // Proxy route for remote WebGPU clustering service

View File

@ -1,6 +1,22 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { processSentenceEmbeddings, SentenceEmbedding } from '@/lib/text-processor'; import { processSentenceEmbeddings, SentenceEmbedding } from '@/lib/text-processor';
import { PineconeService } from '@/lib/pinecone'; import { QdrantService } from '@/lib/qdrant';
/** /**
* API endpoint for splitting text into sentences and generating embeddings * API endpoint for splitting text into sentences and generating embeddings
@ -49,7 +65,7 @@ export async function POST(req: NextRequest) {
}); });
// Store in Pinecone // Store in Pinecone
const pineconeService = PineconeService.getInstance(); const pineconeService = QdrantService.getInstance();
await pineconeService.storeEmbeddingsWithMetadata( await pineconeService.storeEmbeddingsWithMetadata(
embeddingsMap, embeddingsMap,
textContentMap, textContentMap,

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { GraphDBType } from '@/lib/graph-db-service'; import { GraphDBType } from '@/lib/graph-db-service';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
// Global flag to track if embeddings generation should be stopped // Global flag to track if embeddings generation should be stopped

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
// Global flag to track if processing should be stopped // Global flag to track if processing should be stopped

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import RAGService from '@/lib/rag'; import RAGService from '@/lib/rag';

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
// Simple test endpoint to verify proxy connectivity // Simple test endpoint to verify proxy connectivity

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
const UNIFIED_GPU_SERVICE_URL = process.env.UNIFIED_GPU_SERVICE_URL || 'http://localhost:8080' const UNIFIED_GPU_SERVICE_URL = process.env.UNIFIED_GPU_SERVICE_URL || 'http://localhost:8080'

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
const UNIFIED_GPU_SERVICE_URL = process.env.UNIFIED_GPU_SERVICE_URL || 'http://localhost:8080' const UNIFIED_GPU_SERVICE_URL = process.env.UNIFIED_GPU_SERVICE_URL || 'http://localhost:8080'

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { LLMService } from '@/lib/llm-service'; import { LLMService } from '@/lib/llm-service';

View File

@ -1,3 +1,19 @@
//
// 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"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";

View File

@ -1,3 +1,19 @@
//
// 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.
//
import { DocumentsTable } from "@/components/documents-table"; import { DocumentsTable } from "@/components/documents-table";
import { DocumentProcessor } from "@/components/document-processor"; import { DocumentProcessor } from "@/components/document-processor";

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
import { useEffect, useState } from "react" import { useEffect, useState } from "react"

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
/** /**

View File

@ -1,3 +1,19 @@
//
// 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.
//
import type React from "react" import type React from "react"
import type { Metadata } from "next" import type { Metadata } from "next"
import { Inter } from "next/font/google" import { Inter } from "next/font/google"
@ -43,9 +59,6 @@ export default function RootLayout({
<NvidiaIcon className="h-8 w-8" /> <NvidiaIcon className="h-8 w-8" />
<div> <div>
<span className="text-xl font-bold gradient-text">txt2kg</span> <span className="text-xl font-bold gradient-text">txt2kg</span>
<span className="ml-2 text-xs bg-primary/20 text-[#76b900] px-2 py-0.5 rounded-full">
Powered by NVIDIA AI
</span>
</div> </div>
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
import { useState, useEffect } from "react" import { useState, useEffect } from "react"

View File

@ -1,3 +1,19 @@
//
// 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"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
@ -17,6 +33,7 @@ interface MetricsData {
recall: number; recall: number;
f1Score: number; f1Score: number;
topQueries: { query: string; count: number }[]; topQueries: { query: string; count: number }[];
queryTimesByMode?: Record<string, number>;
queryLogStats?: { queryLogStats?: {
totalQueryLogs: number; totalQueryLogs: number;
totalExecutions: number; totalExecutions: number;

View File

@ -1,3 +1,19 @@
//
// 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"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
@ -12,6 +28,7 @@ import { ArrowLeft, BarChart2, Search as SearchIcon } from "lucide-react";
export default function RagPage() { export default function RagPage() {
const router = useRouter(); const router = useRouter();
const [results, setResults] = useState<Triple[] | null>(null); const [results, setResults] = useState<Triple[] | null>(null);
const [llmAnswer, setLlmAnswer] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null); const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [vectorEnabled, setVectorEnabled] = useState(false); const [vectorEnabled, setVectorEnabled] = useState(false);
@ -20,6 +37,7 @@ export default function RagPage() {
avgRelevance: number; avgRelevance: number;
precision: number; precision: number;
recall: number; recall: number;
queryTimesByMode?: Record<string, number>;
} | null>(null); } | null>(null);
const [currentParams, setCurrentParams] = useState<RagParams>({ const [currentParams, setCurrentParams] = useState<RagParams>({
kNeighbors: 4096, kNeighbors: 4096,
@ -64,7 +82,8 @@ export default function RagPage() {
avgQueryTime: data.avgQueryTime, avgQueryTime: data.avgQueryTime,
avgRelevance: data.avgRelevance, avgRelevance: data.avgRelevance,
precision: data.precision, precision: data.precision,
recall: data.recall recall: data.recall,
queryTimesByMode: data.queryTimesByMode
}); });
} }
} catch (error) { } catch (error) {
@ -84,12 +103,20 @@ export default function RagPage() {
let resultCount = 0; let resultCount = 0;
let relevanceScore = 0; let relevanceScore = 0;
// Debug logging
console.log('🔍 Query params:', {
usePureRag: params.usePureRag,
useVectorSearch: params.useVectorSearch,
vectorEnabled,
queryMode: params.queryMode
});
try { try {
// If using pure RAG (Pinecone + LangChain) without graph search // If using pure RAG (Pinecone + LangChain) without graph search
if (params.usePureRag) { if (params.usePureRag) {
queryMode = 'pure-rag'; queryMode = 'pure-rag';
try { try {
console.log('Using pure RAG with just Pinecone and LangChain for query:', query); console.log('Using pure RAG with Qdrant and NVIDIA LLM for query:', query);
const ragResponse = await fetch('/api/rag-query', { const ragResponse = await fetch('/api/rag-query', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@ -101,17 +128,22 @@ export default function RagPage() {
if (ragResponse.ok) { if (ragResponse.ok) {
const data = await ragResponse.json(); const data = await ragResponse.json();
console.log('📥 RAG Response data:', {
hasAnswer: !!data.answer,
answerLength: data.answer?.length,
documentCount: data.documentCount
});
// Handle the answer - we might need to display differently than triples // Handle the answer - we might need to display differently than triples
if (data.answer) { if (data.answer) {
// Special UI handling for text answer rather than triples console.log('✅ Setting answer in results:', data.answer.substring(0, 100) + '...');
setResults([{
subject: 'Answer',
predicate: '',
object: data.answer,
usedFallback: data.usedFallback
}]);
resultCount = 1; // Set the LLM answer for display (same as traditional mode)
setLlmAnswer(data.answer);
// Set empty results array since Pure RAG doesn't return triples
setResults([]);
resultCount = data.documentCount || 0;
relevanceScore = data.relevanceScore || 0; relevanceScore = data.relevanceScore || 0;
// Log the query with performance metrics // Log the query with performance metrics
@ -121,7 +153,7 @@ export default function RagPage() {
resultCount resultCount
}); });
console.log('Pure RAG query completed successfully'); console.log(`✅ Pure RAG query completed. Retrieved ${resultCount} document chunks`);
setIsLoading(false); setIsLoading(false);
return; return;
} }
@ -136,8 +168,8 @@ export default function RagPage() {
} }
} }
// If we have vector embeddings, use enhanced query with metadata // If we have vector embeddings AND explicitly selected vector search, use enhanced query with metadata
if (vectorEnabled && params.useVectorSearch) { if (vectorEnabled && params.useVectorSearch && !params.usePureRag) {
queryMode = 'vector-search'; queryMode = 'vector-search';
try { try {
console.log('Using enhanced RAG with LangChain for query:', query); console.log('Using enhanced RAG with LangChain for query:', query);
@ -184,35 +216,71 @@ export default function RagPage() {
} }
} }
// Call the traditional backend API as fallback or if explicitly selected // Call the LLM-enhanced graph query API
console.log('✅ Using Graph Search + LLM approach');
queryMode = 'traditional'; queryMode = 'traditional';
const response = await fetch(`/api/query`, {
// Get selected LLM model from localStorage
let llmModel = undefined;
let llmProvider = undefined;
try {
const savedModel = localStorage.getItem("selectedModelForRAG");
if (savedModel) {
const modelData = JSON.parse(savedModel);
llmModel = modelData.model;
llmProvider = modelData.provider;
console.log(`Using LLM: ${llmModel} (${llmProvider})`);
}
} catch (e) {
console.warn("Could not load selected LLM model, using default");
}
const response = await fetch(`/api/graph-query-llm`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
query, query,
kNeighbors: params.kNeighbors, topK: params.topK || 5,
fanout: params.fanout, useTraditional: true,
numHops: params.numHops, llmModel,
topK: params.topK, llmProvider
queryMode: queryMode, // Explicitly pass the query mode
useTraditional: true // Force use of the direct pattern matching approach
}), }),
}); });
if (!response.ok) { if (!response.ok) {
const errorData = await response.json(); const errorData = await response.json();
throw new Error(errorData.error || 'Failed to query the RAG backend'); throw new Error(errorData.error || 'Failed to query with LLM');
} }
const data = await response.json(); const data = await response.json();
// Update the results // Log sample of retrieved triples for debugging
setResults(data.relevantTriples || []); console.log('📊 Retrieved Triples (sample):', data.triples.slice(0, 3));
console.log('🤖 LLM-Generated Answer (preview):', data.answer?.substring(0, 200) + '...');
console.log('📈 Triple Count:', data.count);
// DEBUG: Check if depth/pathLength are present in received data
if (data.triples && data.triples.length > 0) {
console.log('🔍 First triple structure:', JSON.stringify(data.triples[0], null, 2));
console.log('🔍 Has depth?', 'depth' in data.triples[0]);
console.log('🔍 Has pathLength?', 'pathLength' in data.triples[0]);
}
// Update the results with the triples (for display)
setResults(data.triples || []);
resultCount = data.count || 0; resultCount = data.count || 0;
relevanceScore = data.relevanceScore || 0; relevanceScore = 0; // No relevance score for traditional search
// Store the LLM answer for display
if (data.answer) {
console.log('✅ Setting llmAnswer state (length:', data.answer.length, 'chars)');
setLlmAnswer(data.answer);
} else {
console.log('⚠️ No answer in response');
setLlmAnswer(null);
}
// Log the query with performance metrics // Log the query with performance metrics
logQuery(query, queryMode, { logQuery(query, queryMode, {
@ -279,6 +347,7 @@ export default function RagPage() {
const clearResults = () => { const clearResults = () => {
setResults(null); setResults(null);
setLlmAnswer(null);
setErrorMessage(null); setErrorMessage(null);
}; };
@ -318,22 +387,34 @@ export default function RagPage() {
</div> </div>
<div className="space-y-3 text-sm"> <div className="space-y-3 text-sm">
{/* Query times by mode */}
{metrics.queryTimesByMode && Object.keys(metrics.queryTimesByMode).length > 0 ? (
<>
{metrics.queryTimesByMode['pure-rag'] !== undefined && (
<div className="flex justify-between">
<span className="text-muted-foreground">Pure RAG:</span>
<span className="font-medium">{(metrics.queryTimesByMode['pure-rag'] / 1000).toFixed(2)}s</span>
</div>
)}
{metrics.queryTimesByMode['traditional'] !== undefined && (
<div className="flex justify-between">
<span className="text-muted-foreground">Graph Search:</span>
<span className="font-medium">{(metrics.queryTimesByMode['traditional'] / 1000).toFixed(2)}s</span>
</div>
)}
{metrics.queryTimesByMode['vector-search'] !== undefined && (
<div className="flex justify-between">
<span className="text-muted-foreground">GraphRAG:</span>
<span className="font-medium">{(metrics.queryTimesByMode['vector-search'] / 1000).toFixed(2)}s</span>
</div>
)}
</>
) : (
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">Avg. Query Time:</span> <span className="text-muted-foreground">Avg. Query Time:</span>
<span className="font-medium">{metrics.avgQueryTime > 0 ? `${metrics.avgQueryTime.toFixed(2)}ms` : "No data"}</span> <span className="font-medium">{metrics.avgQueryTime > 0 ? `${metrics.avgQueryTime.toFixed(2)}ms` : "No data"}</span>
</div> </div>
<div className="flex justify-between"> )}
<span className="text-muted-foreground">Relevance Score:</span>
<span className="font-medium">{metrics.avgRelevance > 0 ? `${(metrics.avgRelevance * 100).toFixed(1)}%` : "No data"}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Precision:</span>
<span className="font-medium">{metrics.precision > 0 ? `${(metrics.precision * 100).toFixed(1)}%` : "No data"}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Recall:</span>
<span className="font-medium">{metrics.recall > 0 ? `${(metrics.recall * 100).toFixed(1)}%` : "No data"}</span>
</div>
</div> </div>
</div> </div>
)} )}
@ -349,14 +430,81 @@ export default function RagPage() {
vectorEnabled={vectorEnabled} vectorEnabled={vectorEnabled}
/> />
{/* LLM Answer Section */}
{llmAnswer && (
<div className="mt-8 nvidia-build-card">
<div className="flex items-center gap-3 mb-4">
<div className="w-6 h-6 rounded-md bg-nvidia-green/15 flex items-center justify-center">
<SearchIcon className="h-3 w-3 text-nvidia-green" />
</div>
<h3 className="text-lg font-semibold text-foreground">Answer</h3>
{currentParams.queryMode && (
<span className="text-xs px-2.5 py-1 rounded-full font-medium bg-nvidia-green/10 text-nvidia-green border border-nvidia-green/20">
{currentParams.queryMode === 'pure-rag' ? 'Pure RAG' :
currentParams.queryMode === 'vector-search' ? 'GraphRAG' :
'Graph Search'}
</span>
)}
</div>
<div className="prose prose-sm dark:prose-invert max-w-none">
{(() => {
// Parse <think> tags
const thinkMatch = llmAnswer.match(/<think>([\s\S]*?)<\/think>/);
const thinkContent = thinkMatch ? thinkMatch[1].trim() : null;
const mainAnswer = thinkContent
? llmAnswer.replace(/<think>[\s\S]*?<\/think>/, '').trim()
: llmAnswer;
return (
<>
{thinkContent && (
<details className="mb-4 bg-muted/10 border border-border/20 rounded-xl overflow-hidden group">
<summary className="cursor-pointer p-4 hover:bg-muted/20 transition-colors flex items-center gap-2">
<svg className="w-4 h-4 transform transition-transform group-open:rotate-90" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
<span className="text-sm font-medium text-muted-foreground">Reasoning Process</span>
</summary>
<div className="p-4 pt-0 text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap border-t border-border/10">
{thinkContent}
</div>
</details>
)}
<div className="bg-muted/20 border border-border/20 p-6 rounded-xl">
<div
className="text-foreground leading-relaxed whitespace-pre-wrap"
dangerouslySetInnerHTML={{
__html: mainAnswer
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
}}
/>
</div>
</>
);
})()}
</div>
</div>
)}
{/* Results Section */} {/* Results Section */}
{results && results.length > 0 && ( {results && results.length > 0 && !currentParams.usePureRag && (
<div className="mt-8 nvidia-build-card"> <div className="mt-8 nvidia-build-card">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-6 h-6 rounded-md bg-nvidia-green/15 flex items-center justify-center"> <div className="w-6 h-6 rounded-md bg-nvidia-green/15 flex items-center justify-center">
<SearchIcon className="h-3 w-3 text-nvidia-green" /> <SearchIcon className="h-3 w-3 text-nvidia-green" />
</div> </div>
<h3 className="text-lg font-semibold text-foreground">Results ({results.length})</h3> <h3 className="text-lg font-semibold text-foreground">
{llmAnswer ? `Retrieved Knowledge (${results.length})` : `Results (${results.length})`}
</h3>
{results.some((r: any) => r.pathLength && r.pathLength > 1) && (
<span className="text-xs px-2.5 py-1 rounded-full font-medium bg-amber-500/10 text-amber-600 dark:text-amber-400 border border-amber-500/20 flex items-center gap-1.5">
<svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
Multi-hop enabled
</span>
)}
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
{results.map((triple, index) => ( {results.map((triple, index) => (
@ -389,8 +537,33 @@ export default function RagPage() {
</div> </div>
)} )}
{triple.confidence && !currentParams.usePureRag && ( {triple.confidence && !currentParams.usePureRag && (
<div className="mt-2 text-xs text-muted-foreground"> <div className="mt-3 flex items-center gap-4 text-xs">
Confidence: {(triple.confidence * 100).toFixed(1)}% <div className="flex items-center gap-1.5">
<div className="w-2 h-2 rounded-full bg-nvidia-green/60"></div>
<span className="text-muted-foreground">
Confidence: <span className="font-medium text-foreground">{(triple.confidence * 100).toFixed(1)}%</span>
</span>
</div>
{triple.depth !== undefined && (
<div className="flex items-center gap-1.5">
<svg className="w-3 h-3 text-nvidia-green/60" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7l5 5m0 0l-5 5m5-5H6" />
</svg>
<span className="text-muted-foreground">
Hop: <span className="font-medium text-foreground">{triple.depth + 1}</span>
</span>
</div>
)}
{triple.pathLength !== undefined && triple.pathLength > 1 && (
<div className="flex items-center gap-1.5">
<svg className="w-3 h-3 text-amber-500/60" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
</svg>
<span className="text-amber-600/80 dark:text-amber-400/80">
Multi-hop path (length: <span className="font-medium">{triple.pathLength}</span>)
</span>
</div>
)}
</div> </div>
)} )}
</div> </div>
@ -399,7 +572,7 @@ export default function RagPage() {
</div> </div>
)} )}
{results && results.length === 0 && !isLoading && ( {results && results.length === 0 && !isLoading && !currentParams.usePureRag && (
<div className="mt-8 nvidia-build-card border-dashed"> <div className="mt-8 nvidia-build-card border-dashed">
<div className="text-center py-8"> <div className="text-center py-8">
<div className="w-12 h-12 rounded-xl bg-muted/30 flex items-center justify-center mx-auto mb-4"> <div className="w-12 h-12 rounded-xl bg-muted/30 flex items-center justify-center mx-auto mb-4">

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
import React, { useState, useEffect } from "react" import React, { useState, useEffect } from "react"

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
import React, { useState, useEffect, useCallback, useMemo } from "react" import React, { useState, useEffect, useCallback, useMemo } from "react"

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
// @ts-nocheck // @ts-nocheck

View File

@ -1,3 +1,19 @@
//
// 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.
//
import React, { useState } from "react"; import React, { useState } from "react";
import { ChevronDown, ChevronRight } from "lucide-react"; import { ChevronDown, ChevronRight } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
import type React from "react" import type React from "react"

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
import { useEffect } from "react" import { useEffect } from "react"

View File

@ -1,7 +1,23 @@
//
// 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" "use client"
import { useState, useEffect } from "react" import { useState, useEffect } from "react"
import { Network, Database, Zap, AlertCircle, RefreshCw, ChevronDown, ChevronUp, InfoIcon, Trash2 } from "lucide-react" import { Network, Database, Zap, AlertCircle, RefreshCw, ChevronDown, ChevronUp, InfoIcon, Trash2, LogOut } from "lucide-react"
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
@ -163,8 +179,13 @@ export function DatabaseConnection({ className }: DatabaseConnectionProps) {
nodes: typeof data.totalVectorCount === 'number' ? data.totalVectorCount : 0, nodes: typeof data.totalVectorCount === 'number' ? data.totalVectorCount : 0,
relationships: 0, // Vector DB doesn't store relationships relationships: 0, // Vector DB doesn't store relationships
source: data.source || 'unknown', source: data.source || 'unknown',
httpHealthy: data.httpHealthy httpHealthy: data.httpHealthy,
}); // Store additional Qdrant stats
...(data.status && { status: data.status }),
...(data.vectorSize && { vectorSize: data.vectorSize }),
...(data.distance && { distance: data.distance }),
...(data.url && { url: data.url }),
} as any);
// If we have a healthy HTTP connection, we're connected // If we have a healthy HTTP connection, we're connected
if (data.httpHealthy) { if (data.httpHealthy) {
@ -364,39 +385,65 @@ export function DatabaseConnection({ className }: DatabaseConnectionProps) {
)} )}
<div className="flex gap-2"> <div className="flex gap-2">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={checkGraphConnection} onClick={checkGraphConnection}
disabled={graphConnectionStatus === "checking"} disabled={graphConnectionStatus === "checking"}
className="flex-1 text-xs h-7" className="flex-1 text-xs h-7 px-2"
> >
{graphConnectionStatus === "checking" ? "Checking..." : "Refresh"} <RefreshCw className={`h-3 w-3 ${graphConnectionStatus === "checking" ? "animate-spin" : ""}`} />
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>{graphConnectionStatus === "checking" ? "Checking..." : "Refresh connection"}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{graphConnectionStatus === "connected" ? ( {graphConnectionStatus === "connected" ? (
<> <>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={disconnectGraph} onClick={disconnectGraph}
className="flex-1 text-xs h-7" className="flex-1 text-xs h-7 px-2"
> >
Disconnect <LogOut className="h-3 w-3" />
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Disconnect</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Dialog open={showClearDialog} onOpenChange={setShowClearDialog}> <Dialog open={showClearDialog} onOpenChange={setShowClearDialog}>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
variant="destructive" variant="destructive"
size="sm" size="sm"
className="flex-1 text-xs h-7" className="flex-1 text-xs h-7 px-2"
disabled={isClearingDB} disabled={isClearingDB}
> >
<Trash2 className="h-3 w-3 mr-1" /> <Trash2 className="h-3 w-3" />
Clear
</Button> </Button>
</DialogTrigger> </DialogTrigger>
</TooltipTrigger>
<TooltipContent>
<p>Clear database</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle className="text-destructive">Clear Database</DialogTitle> <DialogTitle className="text-destructive">Clear Database</DialogTitle>
@ -487,21 +534,33 @@ export function DatabaseConnection({ className }: DatabaseConnectionProps) {
<> <>
<div className="flex items-center gap-2 text-xs md:text-sm"> <div className="flex items-center gap-2 text-xs md:text-sm">
<span className="text-foreground font-medium"> <span className="text-foreground font-medium">
Pinecone Qdrant
</span> </span>
<span className="text-foreground font-mono text-[11px] bg-secondary/50 px-2 py-0.5 rounded truncate max-w-full"> <span className="text-foreground font-mono text-[11px] bg-secondary/50 px-2 py-0.5 rounded truncate max-w-full">
direct-http {(vectorStats as any).url || 'http://qdrant:6333'}
</span> </span>
</div> </div>
{vectorStats.nodes > 0 && ( <div className="text-xs md:text-sm text-muted-foreground space-y-1">
<div className="text-xs md:text-sm text-muted-foreground">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Database className="h-3.5 w-3.5" /> <Database className="h-3.5 w-3.5" />
<span>{vectorStats.nodes.toLocaleString()} vectors</span> <span>{vectorStats.nodes.toLocaleString()} vectors indexed</span>
</div> </div>
{(vectorStats as any).status && (
<div className="flex items-center gap-2">
<Zap className="h-3.5 w-3.5" />
<span>Status: <span className="capitalize">{(vectorStats as any).status}</span></span>
</div> </div>
)} )}
{(vectorStats as any).vectorSize && (
<div className="flex items-center gap-2">
<InfoIcon className="h-3.5 w-3.5" />
<span>{(vectorStats as any).vectorSize}d ({(vectorStats as any).distance})</span>
</div>
)}
</div>
</> </>
)} )}
@ -520,51 +579,77 @@ export function DatabaseConnection({ className }: DatabaseConnectionProps) {
)} )}
<div className="flex gap-2"> <div className="flex gap-2">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={checkVectorConnection} onClick={checkVectorConnection}
disabled={vectorConnectionStatus === "checking"} disabled={vectorConnectionStatus === "checking"}
className="flex-1 text-xs h-7" className="flex-1 text-xs h-7 px-2"
> >
{vectorConnectionStatus === "checking" ? "Checking..." : "Refresh"} <RefreshCw className={`h-3 w-3 ${vectorConnectionStatus === "checking" ? "animate-spin" : ""}`} />
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>{vectorConnectionStatus === "checking" ? "Checking..." : "Refresh connection"}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{vectorConnectionStatus === "connected" ? ( {vectorConnectionStatus === "connected" ? (
<> <>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={disconnectVector} onClick={disconnectVector}
className="flex-1 text-xs h-7" className="flex-1 text-xs h-7 px-2"
> >
Disconnect <LogOut className="h-3 w-3" />
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Disconnect</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Dialog open={showClearVectorDialog} onOpenChange={setShowClearVectorDialog}> <Dialog open={showClearVectorDialog} onOpenChange={setShowClearVectorDialog}>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
variant="destructive" variant="destructive"
size="sm" size="sm"
className="flex-1 text-xs h-7" className="flex-1 text-xs h-7 px-2"
disabled={isClearingVectorDB} disabled={isClearingVectorDB}
> >
<Trash2 className="h-3 w-3 mr-1" /> <Trash2 className="h-3 w-3" />
Clear
</Button> </Button>
</DialogTrigger> </DialogTrigger>
</TooltipTrigger>
<TooltipContent>
<p>Clear database</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle className="text-destructive">Clear Pinecone Database</DialogTitle> <DialogTitle className="text-destructive">Clear Qdrant Database</DialogTitle>
<DialogDescription> <DialogDescription>
Are you sure you want to clear all data from the Pinecone database? This action cannot be undone. Are you sure you want to clear all data from the Qdrant vector database? This action cannot be undone.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<Alert variant="destructive" className="mt-2"> <Alert variant="destructive" className="mt-2">
<AlertCircle className="h-4 w-4" /> <AlertCircle className="h-4 w-4" />
<AlertTitle>Warning</AlertTitle> <AlertTitle>Warning</AlertTitle>
<AlertDescription> <AlertDescription>
This will permanently delete all vectors from the Pinecone database. This will permanently delete all vectors from the Qdrant database.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
<DialogFooter className="gap-2 mt-4"> <DialogFooter className="gap-2 mt-4">

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
import { FC, useState, useEffect } from 'react' import { FC, useState, useEffect } from 'react'

View File

@ -1,3 +1,19 @@
//
// 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"; "use client";
import { useState } from "react"; import { useState } from "react";

View File

@ -1,20 +1,30 @@
//
// 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" "use client"
import { useState, useEffect } from "react" import { useState, useEffect } from "react"
import { useDocuments } from "@/contexts/document-context" import { useDocuments } from "@/contexts/document-context"
import { CheckCircle, Loader2, FileText, AlertCircle, X } from "lucide-react" import { CheckCircle, Loader2, FileText, AlertCircle, X } from "lucide-react"
import { Switch } from "@/components/ui/switch"
import { Label } from "@/components/ui/label"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { useShiftSelect } from "@/hooks/use-shift-select" import { useShiftSelect } from "@/hooks/use-shift-select"
export function DocumentSelection() { export function DocumentSelection() {
const { documents, processDocuments, isProcessing } = useDocuments() const { documents, processDocuments, isProcessing } = useDocuments()
const [useLangChain, setUseLangChain] = useState(false)
const [useSentenceChunking, setUseSentenceChunking] = useState(true)
const [useEntityExtraction, setUseEntityExtraction] = useState(true)
const [processingStatus, setProcessingStatus] = useState("") const [processingStatus, setProcessingStatus] = useState("")
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [forceUpdate, setForceUpdate] = useState(0) const [forceUpdate, setForceUpdate] = useState(0)
@ -108,7 +118,7 @@ export function DocumentSelection() {
// Call processDocuments with the selected document IDs // Call processDocuments with the selected document IDs
await processDocuments(selectedDocs, { await processDocuments(selectedDocs, {
useLangChain, useLangChain: false,
useGraphTransformer: false, useGraphTransformer: false,
promptConfigs: undefined promptConfigs: undefined
}) })
@ -157,65 +167,6 @@ export function DocumentSelection() {
<h3 className="text-md font-medium">Document Selection</h3> <h3 className="text-md font-medium">Document Selection</h3>
<p className="text-sm text-muted-foreground mb-2">Select which documents to process for triple extraction</p> <p className="text-sm text-muted-foreground mb-2">Select which documents to process for triple extraction</p>
<div className="space-y-3 pt-2 mb-4">
<h4 className="text-sm font-medium">Processing Options</h4>
<div className="space-y-2">
<div className="flex items-center space-x-2">
<Switch
id="use-langchain"
checked={useLangChain}
onCheckedChange={(value) => {
setUseLangChain(value);
// Dispatch custom event to update other components
window.dispatchEvent(new CustomEvent('langChainToggled', {
detail: { useLangChain: value }
}));
}}
disabled={isProcessing}
/>
<Label htmlFor="use-langchain" className="text-sm cursor-pointer">Use LangChain</Label>
</div>
{/* <p className="text-xs text-muted-foreground pl-7">
Leverages LangChain for knowledge extraction from documents
</p> */}
{useLangChain && (
<>
<div className="flex items-center space-x-2">
<Switch
id="use-sentence-chunking"
checked={useSentenceChunking}
onCheckedChange={setUseSentenceChunking}
disabled={isProcessing}
/>
<Label htmlFor="use-sentence-chunking" className="text-sm cursor-pointer">
Use Sentence Chunking
</Label>
</div>
<p className="text-xs text-muted-foreground pl-7">
Split documents into sentences for more accurate triple extraction
</p>
<div className="flex items-center space-x-2">
<Switch
id="use-entity-extraction"
checked={useEntityExtraction}
onCheckedChange={setUseEntityExtraction}
disabled={isProcessing}
/>
<Label htmlFor="use-entity-extraction" className="text-sm cursor-pointer">
Entity Extraction
</Label>
</div>
<p className="text-xs text-muted-foreground pl-7">
Automatically detect and extract entities from documents
</p>
</>
)}
</div>
</div>
{error && ( {error && (
<div className="bg-destructive/10 border border-destructive rounded-md p-4 flex items-start gap-3"> <div className="bg-destructive/10 border border-destructive rounded-md p-4 flex items-start gap-3">
<AlertCircle className="h-5 w-5 text-destructive mt-0.5 flex-shrink-0" /> <AlertCircle className="h-5 w-5 text-destructive mt-0.5 flex-shrink-0" />

View File

@ -1,3 +1,19 @@
//
// 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" "use client"
import { useState } from "react" import { useState } from "react"

Some files were not shown because too many files have changed in this diff Show More