Spaces:
Paused
Paused
Fix ZeroGPU: use direct decorator pattern per HF docs
Browse files
README.md
CHANGED
|
@@ -8,7 +8,6 @@ sdk_version: 6.2.0
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: apache-2.0
|
| 11 |
-
hardware: zero-a10g
|
| 12 |
short_description: Find anomalies in logs using AI. Paste logs, get insights.
|
| 13 |
tags:
|
| 14 |
- log-analysis
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: apache-2.0
|
|
|
|
| 11 |
short_description: Find anomalies in logs using AI. Paste logs, get insights.
|
| 12 |
tags:
|
| 13 |
- log-analysis
|
app.py
CHANGED
|
@@ -1,49 +1,17 @@
|
|
| 1 |
"""
|
| 2 |
Cordon - Semantic Anomaly Detection for Log Files
|
| 3 |
-
HuggingFace Space Demo
|
| 4 |
"""
|
| 5 |
|
| 6 |
import tempfile
|
| 7 |
from pathlib import Path
|
| 8 |
|
| 9 |
import gradio as gr
|
|
|
|
| 10 |
import torch
|
| 11 |
|
| 12 |
from cordon import AnalysisConfig, SemanticLogAnalyzer
|
| 13 |
|
| 14 |
-
# ZeroGPU support with fallback for local development
|
| 15 |
-
try:
|
| 16 |
-
import spaces
|
| 17 |
-
ZEROGPU_AVAILABLE = True
|
| 18 |
-
except ImportError:
|
| 19 |
-
ZEROGPU_AVAILABLE = False
|
| 20 |
-
|
| 21 |
-
# Detect available device
|
| 22 |
-
def get_device() -> str:
|
| 23 |
-
"""Detect best available device."""
|
| 24 |
-
if torch.cuda.is_available():
|
| 25 |
-
return "cuda"
|
| 26 |
-
elif torch.backends.mps.is_available():
|
| 27 |
-
return "mps"
|
| 28 |
-
return "cpu"
|
| 29 |
-
|
| 30 |
-
# Global model cache (keyed by model_name:device)
|
| 31 |
-
_analyzer_cache: dict[str, SemanticLogAnalyzer] = {}
|
| 32 |
-
|
| 33 |
-
def get_cached_analyzer(config: AnalysisConfig) -> SemanticLogAnalyzer:
|
| 34 |
-
"""Get or create a cached analyzer instance.
|
| 35 |
-
|
| 36 |
-
Caches the analyzer to avoid repeated model loading.
|
| 37 |
-
The config is updated on the cached instance for each call.
|
| 38 |
-
"""
|
| 39 |
-
cache_key = f"{config.model_name}:{config.device}"
|
| 40 |
-
if cache_key not in _analyzer_cache:
|
| 41 |
-
_analyzer_cache[cache_key] = SemanticLogAnalyzer(config)
|
| 42 |
-
else:
|
| 43 |
-
# Update config params (window_size, k_neighbors, etc.)
|
| 44 |
-
_analyzer_cache[cache_key].config = config
|
| 45 |
-
return _analyzer_cache[cache_key]
|
| 46 |
-
|
| 47 |
# Load sample log file
|
| 48 |
SAMPLE_LOG_PATH = Path(__file__).parent / "sample_apache.log"
|
| 49 |
FALLBACK_LOG_PATH = Path(__file__).parent.parent / "examples" / "apache_sample.log"
|
|
@@ -63,7 +31,8 @@ def estimate_tokens(text: str) -> int:
|
|
| 63 |
return len(text) // 4 if text else 0
|
| 64 |
|
| 65 |
|
| 66 |
-
|
|
|
|
| 67 |
log_content: str,
|
| 68 |
window_size: int,
|
| 69 |
k_neighbors: int,
|
|
@@ -73,7 +42,11 @@ def _analyze_logs_impl(
|
|
| 73 |
range_max: float,
|
| 74 |
progress: gr.Progress = gr.Progress(),
|
| 75 |
) -> tuple[str, str]:
|
| 76 |
-
"""Analyze log content and return results.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
if not log_content.strip():
|
| 79 |
return "", "Anomalous Sections"
|
|
@@ -88,7 +61,8 @@ def _analyze_logs_impl(
|
|
| 88 |
try:
|
| 89 |
progress(0.1, desc="Configuring analyzer...")
|
| 90 |
|
| 91 |
-
|
|
|
|
| 92 |
# Use larger batch size on GPU for better throughput
|
| 93 |
batch_size = 128 if device == "cuda" else 32
|
| 94 |
|
|
@@ -111,25 +85,12 @@ def _analyze_logs_impl(
|
|
| 111 |
)
|
| 112 |
|
| 113 |
progress(0.15, desc="Loading embedding model...")
|
| 114 |
-
analyzer =
|
| 115 |
-
|
| 116 |
-
progress(0.25, desc="Segmenting log into windows...")
|
| 117 |
-
# Small delay to show progress
|
| 118 |
-
import time
|
| 119 |
-
time.sleep(0.1)
|
| 120 |
|
| 121 |
-
progress(0.4, desc="
|
| 122 |
-
time.sleep(0.1)
|
| 123 |
-
|
| 124 |
-
progress(0.6, desc="Scoring anomalies...")
|
| 125 |
-
time.sleep(0.1)
|
| 126 |
-
|
| 127 |
-
progress(0.75, desc="Analyzing patterns...")
|
| 128 |
result = analyzer.analyze_file_detailed(temp_path)
|
| 129 |
|
| 130 |
progress(0.9, desc="Formatting output...")
|
| 131 |
-
time.sleep(0.05)
|
| 132 |
-
|
| 133 |
progress(1.0, desc="Complete!")
|
| 134 |
|
| 135 |
# Calculate token counts
|
|
@@ -145,13 +106,6 @@ def _analyze_logs_impl(
|
|
| 145 |
temp_path.unlink(missing_ok=True)
|
| 146 |
|
| 147 |
|
| 148 |
-
# Apply ZeroGPU decorator if available, otherwise use the function directly
|
| 149 |
-
if ZEROGPU_AVAILABLE:
|
| 150 |
-
analyze_logs = spaces.GPU(duration=120)(_analyze_logs_impl)
|
| 151 |
-
else:
|
| 152 |
-
analyze_logs = _analyze_logs_impl
|
| 153 |
-
|
| 154 |
-
|
| 155 |
CUSTOM_CSS = """
|
| 156 |
.main, .wrap, .contain, .gradio-container, .app {
|
| 157 |
max-width: 100% !important;
|
|
|
|
| 1 |
"""
|
| 2 |
Cordon - Semantic Anomaly Detection for Log Files
|
| 3 |
+
HuggingFace Space Demo (ZeroGPU)
|
| 4 |
"""
|
| 5 |
|
| 6 |
import tempfile
|
| 7 |
from pathlib import Path
|
| 8 |
|
| 9 |
import gradio as gr
|
| 10 |
+
import spaces
|
| 11 |
import torch
|
| 12 |
|
| 13 |
from cordon import AnalysisConfig, SemanticLogAnalyzer
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
# Load sample log file
|
| 16 |
SAMPLE_LOG_PATH = Path(__file__).parent / "sample_apache.log"
|
| 17 |
FALLBACK_LOG_PATH = Path(__file__).parent.parent / "examples" / "apache_sample.log"
|
|
|
|
| 31 |
return len(text) // 4 if text else 0
|
| 32 |
|
| 33 |
|
| 34 |
+
@spaces.GPU(duration=120)
|
| 35 |
+
def analyze_logs(
|
| 36 |
log_content: str,
|
| 37 |
window_size: int,
|
| 38 |
k_neighbors: int,
|
|
|
|
| 42 |
range_max: float,
|
| 43 |
progress: gr.Progress = gr.Progress(),
|
| 44 |
) -> tuple[str, str]:
|
| 45 |
+
"""Analyze log content and return results.
|
| 46 |
+
|
| 47 |
+
This function is decorated with @spaces.GPU to run on ZeroGPU.
|
| 48 |
+
The GPU is allocated when this function is called.
|
| 49 |
+
"""
|
| 50 |
|
| 51 |
if not log_content.strip():
|
| 52 |
return "", "Anomalous Sections"
|
|
|
|
| 61 |
try:
|
| 62 |
progress(0.1, desc="Configuring analyzer...")
|
| 63 |
|
| 64 |
+
# Check if CUDA is available (should be True on ZeroGPU)
|
| 65 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 66 |
# Use larger batch size on GPU for better throughput
|
| 67 |
batch_size = 128 if device == "cuda" else 32
|
| 68 |
|
|
|
|
| 85 |
)
|
| 86 |
|
| 87 |
progress(0.15, desc="Loading embedding model...")
|
| 88 |
+
analyzer = SemanticLogAnalyzer(config)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
+
progress(0.4, desc="Analyzing patterns...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
result = analyzer.analyze_file_detailed(temp_path)
|
| 92 |
|
| 93 |
progress(0.9, desc="Formatting output...")
|
|
|
|
|
|
|
| 94 |
progress(1.0, desc="Complete!")
|
| 95 |
|
| 96 |
# Calculate token counts
|
|
|
|
| 106 |
temp_path.unlink(missing_ok=True)
|
| 107 |
|
| 108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
CUSTOM_CSS = """
|
| 110 |
.main, .wrap, .contain, .gradio-container, .app {
|
| 111 |
max-width: 100% !important;
|