LPX commited on
Commit
1af0cb5
·
1 Parent(s): a986afb

major: prep implementtation of vLLM smart agents

Browse files
Files changed (2) hide show
  1. app_mcp.py +98 -173
  2. utils/smart_agents.py +54 -0
app_mcp.py CHANGED
@@ -19,7 +19,10 @@ from utils.minmax import preprocess as minmax_preprocess
19
  from utils.ela import genELA as ELA
20
  from utils.wavelet import wavelet_blocking_noise_estimation
21
  from utils.bitplane import bit_plane_extractor
22
- # from utils.exif import exif_full_dump / currently not working
 
 
 
23
 
24
  from forensics.registry import register_model, MODEL_REGISTRY, ModelEntry
25
 
@@ -302,170 +305,15 @@ def get_consensus_label(results):
302
 
303
  # Update predict_image_with_json to return consensus label
304
 
305
- class ModelWeightManager:
306
- def __init__(self):
307
- self.base_weights = {
308
- "model_1": 0.15, # SwinV2 Based
309
- "model_2": 0.15, # ViT Based
310
- "model_3": 0.15, # SDXL Dataset
311
- "model_4": 0.15, # SDXL + FLUX
312
- "model_5": 0.15, # ViT Based
313
- "model_5b": 0.10, # ViT Based, Newer Dataset
314
- "model_6": 0.10, # Swin, Midj + SDXL
315
- "model_7": 0.05 # ViT
316
- }
317
- self.situation_weights = {
318
- "high_confidence": 1.2, # Boost weights for high confidence predictions
319
- "low_confidence": 0.8, # Reduce weights for low confidence
320
- "conflict": 0.5, # Reduce weights when models disagree
321
- "consensus": 1.5 # Boost weights when models agree
322
- }
323
-
324
- def adjust_weights(self, predictions, confidence_scores):
325
- """Dynamically adjust weights based on prediction patterns"""
326
- adjusted_weights = self.base_weights.copy()
327
-
328
- # Check for consensus
329
- if self._has_consensus(predictions):
330
- for model in adjusted_weights:
331
- adjusted_weights[model] *= self.situation_weights["consensus"]
332
-
333
- # Check for conflicts
334
- if self._has_conflicts(predictions):
335
- for model in adjusted_weights:
336
- adjusted_weights[model] *= self.situation_weights["conflict"]
337
-
338
- # Adjust based on confidence
339
- for model, confidence in confidence_scores.items():
340
- if confidence > 0.8:
341
- adjusted_weights[model] *= self.situation_weights["high_confidence"]
342
- elif confidence < 0.5:
343
- adjusted_weights[model] *= self.situation_weights["low_confidence"]
344
-
345
- return self._normalize_weights(adjusted_weights)
346
-
347
- def _has_consensus(self, predictions):
348
- """Check if models agree on prediction"""
349
- return len(set(predictions.values())) == 1
350
-
351
- def _has_conflicts(self, predictions):
352
- """Check if models have conflicting predictions"""
353
- return len(set(predictions.values())) > 2
354
-
355
- def _normalize_weights(self, weights):
356
- """Normalize weights to sum to 1"""
357
- total = sum(weights.values())
358
- return {k: v/total for k, v in weights.items()}
359
-
360
- class EnsembleMonitorAgent:
361
- def __init__(self):
362
- self.performance_metrics = {
363
- "model_accuracy": {},
364
- "response_times": {},
365
- "confidence_distribution": {},
366
- "consensus_rate": 0.0
367
- }
368
- self.alerts = []
369
-
370
- def monitor_prediction(self, model_id, prediction, confidence, response_time):
371
- """Monitor individual model performance"""
372
- if model_id not in self.performance_metrics["model_accuracy"]:
373
- self.performance_metrics["model_accuracy"][model_id] = []
374
- self.performance_metrics["response_times"][model_id] = []
375
- self.performance_metrics["confidence_distribution"][model_id] = []
376
-
377
- self.performance_metrics["response_times"][model_id].append(response_time)
378
- self.performance_metrics["confidence_distribution"][model_id].append(confidence)
379
-
380
- # Check for performance issues
381
- self._check_performance_issues(model_id)
382
-
383
- def _check_performance_issues(self, model_id):
384
- """Check for any performance anomalies"""
385
- response_times = self.performance_metrics["response_times"][model_id]
386
- if len(response_times) > 10:
387
- avg_time = sum(response_times[-10:]) / 10
388
- if avg_time > 2.0: # More than 2 seconds
389
- self.alerts.append(f"High latency detected for {model_id}: {avg_time:.2f}s")
390
-
391
- class WeightOptimizationAgent:
392
- def __init__(self, weight_manager):
393
- self.weight_manager = weight_manager
394
- self.performance_history = []
395
- self.optimization_threshold = 0.1 # 10% performance change triggers optimization
396
-
397
- def analyze_performance(self, predictions, actual_results):
398
- """Analyze model performance and suggest weight adjustments"""
399
- # Placeholder for actual_results. In a real scenario, this would come from a validation set.
400
- # For now, we'll just track predictions.
401
- self.performance_history.append(predictions)
402
-
403
- if self._should_optimize():
404
- self._optimize_weights()
405
-
406
- def _should_optimize(self):
407
- """Determine if weights should be optimized"""
408
- if len(self.performance_history) < 10:
409
- return False
410
-
411
- # Placeholder for actual performance calculation
412
- # For demonstration, let's say we optimize every 10 runs
413
- return len(self.performance_history) % 10 == 0
414
-
415
- def _optimize_weights(self):
416
- """Optimize model weights based on performance"""
417
- logger.info("Optimizing model weights based on recent performance.")
418
- # This is where more sophisticated optimization logic would go.
419
- # For example, you could slightly adjust weights of models that consistently predict correctly.
420
- pass
421
-
422
- class SystemHealthAgent:
423
- def __init__(self):
424
- self.health_metrics = {
425
- "memory_usage": [],
426
- "gpu_utilization": [],
427
- "model_load_times": {},
428
- "error_rates": {}
429
- }
430
-
431
- def monitor_system_health(self):
432
- """Monitor overall system health"""
433
- self._check_memory_usage()
434
- self._check_gpu_utilization()
435
- # You might add _check_model_health() here later
436
-
437
- def _check_memory_usage(self):
438
- """Monitor memory usage"""
439
- try:
440
- import psutil
441
- memory = psutil.virtual_memory()
442
- self.health_metrics["memory_usage"].append(memory.percent)
443
-
444
- if memory.percent > 90:
445
- logger.warning(f"High memory usage detected: {memory.percent}%")
446
- except ImportError:
447
- logger.warning("psutil not installed. Cannot monitor memory usage.")
448
-
449
- def _check_gpu_utilization(self):
450
- """Monitor GPU utilization if available"""
451
- if torch.cuda.is_available():
452
- try:
453
- gpu_util = torch.cuda.memory_allocated() / torch.cuda.max_memory_allocated()
454
- self.health_metrics["gpu_utilization"].append(gpu_util)
455
-
456
- if gpu_util > 0.9:
457
- logger.warning(f"High GPU utilization detected: {gpu_util*100:.2f}%")
458
- except Exception as e:
459
- logger.warning(f"Error monitoring GPU utilization: {e}")
460
- else:
461
- logger.info("CUDA not available. Skipping GPU utilization monitoring.")
462
-
463
  def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_degrees, noise_level, sharpen_strength):
464
  # Initialize agents
465
  monitor_agent = EnsembleMonitorAgent()
466
  weight_manager = ModelWeightManager()
467
  optimization_agent = WeightOptimizationAgent(weight_manager)
468
  health_agent = SystemHealthAgent()
 
 
 
469
 
470
  # Monitor system health
471
  health_agent.monitor_system_health()
@@ -476,8 +324,8 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
476
  img_pil = img
477
  img_np_og = np.array(img) # Convert PIL Image to NumPy array
478
 
479
- # Get predictions with timing
480
- model_predictions = {}
481
  confidence_scores = {}
482
  results = [] # To store the results for the DataFrame
483
 
@@ -494,24 +342,34 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
494
  model_end - model_start
495
  )
496
 
497
- model_predictions[model_id] = result["Label"]
498
  confidence_scores[model_id] = max(result.get("AI Score", 0.0), result.get("Real Score", 0.0))
499
  results.append(result) # Add individual model result to the list
500
 
501
- # Get adjusted weights
502
- adjusted_weights = weight_manager.adjust_weights(model_predictions, confidence_scores)
503
-
504
- # Optimize weights if needed
505
- optimization_agent.analyze_performance(model_predictions, None) # Placeholder for actual results
 
 
 
 
 
 
 
506
 
507
- # Calculate weighted consensus
 
 
 
508
  weighted_predictions = {
509
  "AI": 0.0,
510
  "REAL": 0.0,
511
  "UNCERTAIN": 0.0
512
  }
513
 
514
- for model_id, prediction in model_predictions.items():
515
  # Ensure the prediction label is valid for weighted_predictions
516
  if prediction in weighted_predictions:
517
  weighted_predictions[prediction] += adjusted_weights[model_id]
@@ -524,8 +382,11 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
524
  final_prediction_label = "AI"
525
  elif weighted_predictions["REAL"] > weighted_predictions["AI"] and weighted_predictions["REAL"] > weighted_predictions["UNCERTAIN"]:
526
  final_prediction_label = "REAL"
 
 
 
527
 
528
- # Rest of your existing code remains the same after this point
529
  gradient_image = gradient_processing(img_np_og) # Added gradient processing
530
  minmax_image = minmax_preprocess(img_np_og) # Added MinMax processing
531
 
@@ -537,7 +398,24 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
537
  ela3 = ELA(img_np_og, quality=75, scale=75, contrast=25, linear=False, grayscale=False)
538
 
539
  forensics_images = [img_pil, ela1, ela2, ela3, gradient_image, minmax_image]
540
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
  # Prepare table rows for Dataframe (exclude model path)
542
  table_rows = [[
543
  r.get("Model", ""),
@@ -549,7 +427,54 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
549
 
550
  # The get_consensus_label function is now replaced by final_prediction_label from weighted consensus
551
  consensus_html = f"<b><span style='color:{'red' if final_prediction_label == 'AI' else ('green' if final_prediction_label == 'REAL' else 'orange')}'>{final_prediction_label}</span></b>"
552
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
553
  return img_pil, forensics_images, table_rows, results, consensus_html
554
 
555
  with gr.Blocks(css="#post-gallery { overflow: hidden !important;} .grid-wrap{ overflow-y: hidden !important;} .ms-gr-ant-welcome-icon{ height:unset !important;} .tabs{margin-top:10px;}") as demo:
@@ -615,7 +540,7 @@ with gr.Blocks(css="#post-gallery { overflow: hidden !important;} .grid-wrap{ ov
615
  outputs=outputs
616
  )
617
  with gr.Tab("🙈 Project Introduction"):
618
- gr.Markdown("# AI Generated / Deepfake Detection Models Leaderboard: Soon™")
619
 
620
  with gr.Tab("👑 Community Forensics Preview"):
621
  temp_space = gr.load("aiwithoutborders-xyz/OpenSight-Community-Forensics-Preview", src="spaces")
 
19
  from utils.ela import genELA as ELA
20
  from utils.wavelet import wavelet_blocking_noise_estimation
21
  from utils.bitplane import bit_plane_extractor
22
+ from utils.hf_logger import log_inference_data
23
+ from utils.weight_management import ContextualWeightOverrideAgent, ModelWeightManager
24
+ from utils.monitoring_agents import EnsembleMonitorAgent, WeightOptimizationAgent, SystemHealthAgent
25
+ from utils.smart_agents import ContextualIntelligenceAgent, ForensicAnomalyDetectionAgent
26
 
27
  from forensics.registry import register_model, MODEL_REGISTRY, ModelEntry
28
 
 
305
 
306
  # Update predict_image_with_json to return consensus label
307
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_degrees, noise_level, sharpen_strength):
309
  # Initialize agents
310
  monitor_agent = EnsembleMonitorAgent()
311
  weight_manager = ModelWeightManager()
312
  optimization_agent = WeightOptimizationAgent(weight_manager)
313
  health_agent = SystemHealthAgent()
314
+ # New smart agents
315
+ context_agent = ContextualIntelligenceAgent()
316
+ anomaly_agent = ForensicAnomalyDetectionAgent()
317
 
318
  # Monitor system health
319
  health_agent.monitor_system_health()
 
324
  img_pil = img
325
  img_np_og = np.array(img) # Convert PIL Image to NumPy array
326
 
327
+ # 1. Get initial predictions from all models
328
+ model_predictions_raw = {}
329
  confidence_scores = {}
330
  results = [] # To store the results for the DataFrame
331
 
 
342
  model_end - model_start
343
  )
344
 
345
+ model_predictions_raw[model_id] = result["Label"]
346
  confidence_scores[model_id] = max(result.get("AI Score", 0.0), result.get("Real Score", 0.0))
347
  results.append(result) # Add individual model result to the list
348
 
349
+ # 2. Infer context tags using ContextualIntelligenceAgent
350
+ image_data_for_context = {
351
+ "width": img.width,
352
+ "height": img.height,
353
+ "mode": img.mode,
354
+ # Add more features like EXIF data if exif_full_dump is used
355
+ }
356
+ detected_context_tags = context_agent.infer_context_tags(image_data_for_context, model_predictions_raw)
357
+ logger.info(f"Detected context tags: {detected_context_tags}")
358
+
359
+ # 3. Get adjusted weights, passing context tags
360
+ adjusted_weights = weight_manager.adjust_weights(model_predictions_raw, confidence_scores, context_tags=detected_context_tags)
361
 
362
+ # 4. Optimize weights if needed
363
+ # `final_prediction_label` is determined AFTER weighted consensus, so analyze_performance will be called later
364
+
365
+ # 5. Calculate weighted consensus
366
  weighted_predictions = {
367
  "AI": 0.0,
368
  "REAL": 0.0,
369
  "UNCERTAIN": 0.0
370
  }
371
 
372
+ for model_id, prediction in model_predictions_raw.items(): # Use raw predictions for weighting
373
  # Ensure the prediction label is valid for weighted_predictions
374
  if prediction in weighted_predictions:
375
  weighted_predictions[prediction] += adjusted_weights[model_id]
 
382
  final_prediction_label = "AI"
383
  elif weighted_predictions["REAL"] > weighted_predictions["AI"] and weighted_predictions["REAL"] > weighted_predictions["UNCERTAIN"]:
384
  final_prediction_label = "REAL"
385
+
386
+ # Call analyze_performance after final_prediction_label is known
387
+ optimization_agent.analyze_performance(final_prediction_label, None)
388
 
389
+ # 6. Perform forensic processing
390
  gradient_image = gradient_processing(img_np_og) # Added gradient processing
391
  minmax_image = minmax_preprocess(img_np_og) # Added MinMax processing
392
 
 
398
  ela3 = ELA(img_np_og, quality=75, scale=75, contrast=25, linear=False, grayscale=False)
399
 
400
  forensics_images = [img_pil, ela1, ela2, ela3, gradient_image, minmax_image]
401
+
402
+ # 7. Generate boilerplate descriptions for forensic outputs for anomaly agent
403
+ forensic_output_descriptions = [
404
+ f"Original augmented image (PIL): {img_pil.width}x{img_pil.height}",
405
+ "ELA analysis (Pass 1): Grayscale error map, quality 75.",
406
+ "ELA analysis (Pass 2): Grayscale error map, quality 75, enhanced contrast.",
407
+ "ELA analysis (Pass 3): Color error map, quality 75, enhanced contrast.",
408
+ "Gradient processing: Highlights edges and transitions.",
409
+ "MinMax processing: Deviations in local pixel values."
410
+ ]
411
+ # You could also add descriptions for Wavelet and Bit Plane if they were dynamic outputs
412
+ # For instance, if wavelet_blocking_noise_estimation had parameters that changed and you wanted to describe them.
413
+
414
+ # 8. Analyze forensic outputs for anomalies using ForensicAnomalyDetectionAgent
415
+ anomaly_detection_results = anomaly_agent.analyze_forensic_outputs(forensic_output_descriptions)
416
+ logger.info(f"Forensic anomaly detection: {anomaly_detection_results["summary"]}")
417
+
418
+
419
  # Prepare table rows for Dataframe (exclude model path)
420
  table_rows = [[
421
  r.get("Model", ""),
 
427
 
428
  # The get_consensus_label function is now replaced by final_prediction_label from weighted consensus
429
  consensus_html = f"<b><span style='color:{'red' if final_prediction_label == 'AI' else ('green' if final_prediction_label == 'REAL' else 'orange')}'>{final_prediction_label}</span></b>"
430
+
431
+ # Prepare data for logging to Hugging Face dataset
432
+ inference_params = {
433
+ "confidence_threshold": confidence_threshold,
434
+ "augment_methods": augment_methods,
435
+ "rotate_degrees": rotate_degrees,
436
+ "noise_level": noise_level,
437
+ "sharpen_strength": sharpen_strength,
438
+ "detected_context_tags": detected_context_tags
439
+ }
440
+
441
+ ensemble_output_data = {
442
+ "final_prediction_label": final_prediction_label,
443
+ "weighted_predictions": weighted_predictions,
444
+ "adjusted_weights": adjusted_weights
445
+ }
446
+
447
+ # Collect agent monitoring data
448
+ agent_monitoring_data_log = {
449
+ "ensemble_monitor": {
450
+ "alerts": monitor_agent.alerts,
451
+ "performance_metrics": monitor_agent.performance_metrics
452
+ },
453
+ "weight_optimization": {
454
+ "prediction_history_length": len(optimization_agent.prediction_history),
455
+ # You might add a summary of recent accuracy here if _calculate_accuracy is exposed
456
+ },
457
+ "system_health": {
458
+ "memory_usage": health_agent.health_metrics["memory_usage"],
459
+ "gpu_utilization": health_agent.health_metrics["gpu_utilization"]
460
+ },
461
+ "context_intelligence": {
462
+ "detected_context_tags": detected_context_tags
463
+ },
464
+ "forensic_anomaly_detection": anomaly_detection_results
465
+ }
466
+
467
+ # Log the inference data
468
+ log_inference_data(
469
+ original_image=img, # Use the original uploaded image
470
+ inference_params=inference_params,
471
+ model_predictions=results, # This already contains detailed results for each model
472
+ ensemble_output=ensemble_output_data,
473
+ forensic_images=forensics_images, # This is the list of PIL images generated by forensic tools
474
+ agent_monitoring_data=agent_monitoring_data_log,
475
+ human_feedback=None # This can be populated later with human review data
476
+ )
477
+
478
  return img_pil, forensics_images, table_rows, results, consensus_html
479
 
480
  with gr.Blocks(css="#post-gallery { overflow: hidden !important;} .grid-wrap{ overflow-y: hidden !important;} .ms-gr-ant-welcome-icon{ height:unset !important;} .tabs{margin-top:10px;}") as demo:
 
540
  outputs=outputs
541
  )
542
  with gr.Tab("🙈 Project Introduction"):
543
+ gr.Markdown(QUICK_INTRO)
544
 
545
  with gr.Tab("👑 Community Forensics Preview"):
546
  temp_space = gr.load("aiwithoutborders-xyz/OpenSight-Community-Forensics-Preview", src="spaces")
utils/smart_agents.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from PIL import Image
3
+
4
+ logger = logging.getLogger(__name__)
5
+
6
+ class ContextualIntelligenceAgent:
7
+ def __init__(self):
8
+ # In a real scenario, this would involve an LLM call or a sophisticated rule engine
9
+ pass
10
+
11
+ def infer_context_tags(self, image_data: dict, initial_predictions: dict) -> list[str]:
12
+ """Simulates an LLM inferring context tags based on image data and predictions."""
13
+ context_tags = []
14
+
15
+ # Boilerplate logic: infer tags based on simple cues
16
+ if image_data.get("width", 0) > 1000 and image_data.get("height", 0) > 1000:
17
+ context_tags.append("high_resolution")
18
+
19
+ # Example based on initial broad prediction (e.g., if any model strongly predicts 'real')
20
+ if any(v.get("Real Score", 0) > 0.9 for v in initial_predictions.values()):
21
+ context_tags.append("potentially_natural_scene")
22
+
23
+ # Mock external detection (e.g., from a simpler scene classification model or EXIF data)
24
+ # For demonstration, we'll hardcode some possible tags here.
25
+ # In a real system, you'd feed actual image features or metadata to an LLM.
26
+ mock_tags = ["outdoor", "sunny"] # These could be returned by an actual LLM based on input
27
+ for tag in mock_tags:
28
+ if tag not in context_tags:
29
+ context_tags.append(tag)
30
+
31
+ return context_tags
32
+
33
+ class ForensicAnomalyDetectionAgent:
34
+ def __init__(self):
35
+ # In a real scenario, this would involve an LLM call to analyze textual descriptions
36
+ pass
37
+
38
+ def analyze_forensic_outputs(self, forensic_output_descriptions: list[str]) -> dict:
39
+ """Simulates an LLM analyzing descriptions of forensic images for anomalies."""
40
+ anomalies = {"summary": "No significant anomalies detected.", "details": []}
41
+
42
+ # Boilerplate logic: look for keywords in descriptions
43
+ for desc in forensic_output_descriptions:
44
+ if "strong edges" in desc.lower() and "ela" in desc.lower():
45
+ anomalies["summary"] = "Potential manipulation indicated by ELA."
46
+ anomalies["details"].append("ELA: Unusually strong edges detected, suggesting image compositing.")
47
+ if "unexpected patterns" in desc.lower() and "bit plane" in desc.lower():
48
+ anomalies["summary"] = "Anomalies detected in bit plane data."
49
+ anomalies["details"].append("Bit Plane: Irregular patterns found, possibly indicating hidden data or processing.")
50
+
51
+ if len(anomalies["details"]) > 0:
52
+ anomalies["summary"] = "Multiple anomalies detected across forensic outputs."
53
+
54
+ return anomalies