anky2002 commited on
Commit
6677ded
Β·
verified Β·
1 Parent(s): f16ef2b

Fix Bug 3: Filter ghost agents from reasoning tree, add MACRO_DSLR to modality label map, add debug logging for agent scores

Browse files
Files changed (1) hide show
  1. app.py +53 -13
app.py CHANGED
@@ -50,6 +50,9 @@ def run_all_agents(img: Image.Image) -> Tuple[List[AgentEvidence], ForensicVerdi
50
  from agents.modality_detector import detect_modality
51
  modality = detect_modality(img)
52
  adj = modality.score_adjustments
 
 
 
53
 
54
  # Signal processing agents (fast, run in parallel with modality adjustments)
55
  signal_agents = [
@@ -78,6 +81,7 @@ def run_all_agents(img: Image.Image) -> Tuple[List[AgentEvidence], ForensicVerdi
78
  try:
79
  results[name] = future.result()
80
  except Exception as e:
 
81
  results[name] = AgentEvidence(
82
  agent_name=f"{name.title()} Agent (Error)",
83
  violation_score=0.0,
@@ -97,9 +101,25 @@ def run_all_agents(img: Image.Image) -> Tuple[List[AgentEvidence], ForensicVerdi
97
  results.get("text"),
98
  ]
99
  ordered = [r for r in ordered if r is not None]
100
-
101
- # Bayesian synthesis
102
- verdict = bayesian_synthesis(ordered)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  # Attach modality info to verdict for reporting β€” include ALL indicators for diagnostics
105
  verdict.reasoning_tree["modality"] = {
@@ -109,14 +129,22 @@ def run_all_agents(img: Image.Image) -> Tuple[List[AgentEvidence], ForensicVerdi
109
  if not isinstance(v, np.ndarray) and k != "modality_scores"},
110
  "modality_scores": modality.indicators.get("modality_scores", {}),
111
  "adjustments_applied": len(modality.score_adjustments),
112
- "adjustments_list": list(modality.score_adjustments.keys())[:10], # First 10 for display
113
  }
 
 
 
 
 
 
 
114
 
115
  # Generate explanations
116
  verdict.forensic_report = generate_forensic_report(verdict)
117
  reasoning = generate_reasoning_tree(verdict)
118
  verdict.court_brief = generate_court_brief(verdict)
119
 
 
120
  return ordered, verdict, reasoning
121
 
122
 
@@ -174,7 +202,6 @@ def create_radar_chart(agent_results: List[AgentEvidence]) -> go.Figure:
174
  for agent in agent_results:
175
  short_name = agent.agent_name.replace(" Agent", "").replace(" Characteristics", "")
176
  names.append(short_name)
177
- # Map -1..+1 to 0..100 for display
178
  display_score = (agent.violation_score + 1) * 50
179
  scores.append(display_score)
180
  if agent.violation_score > 0.2:
@@ -184,7 +211,6 @@ def create_radar_chart(agent_results: List[AgentEvidence]) -> go.Figure:
184
  else:
185
  colors.append("gold")
186
 
187
- # Close the polygon
188
  names_closed = names + [names[0]]
189
  scores_closed = scores + [scores[0]]
190
 
@@ -199,7 +225,6 @@ def create_radar_chart(agent_results: List[AgentEvidence]) -> go.Figure:
199
  name="Violation Score",
200
  ))
201
 
202
- # Add neutral line at 50 (score = 0)
203
  fig.add_trace(go.Scatterpolar(
204
  r=[50] * (len(names) + 1),
205
  theta=names_closed,
@@ -297,7 +322,6 @@ def create_fft_display(agent_results: List[AgentEvidence]) -> go.Figure:
297
  yaxis=dict(showticklabels=False, scaleanchor="x"),
298
  )
299
  return fig
300
- # Return empty figure
301
  fig = go.Figure()
302
  fig.update_layout(height=400, title="FFT Spectrum (not available)")
303
  return fig
@@ -431,9 +455,18 @@ def analyze_image(img):
431
  mod_name = mod_info.get("detected", "UNKNOWN")
432
  mod_conf = mod_info.get("confidence", 0)
433
  mod_adj = mod_info.get("adjustments_applied", 0)
434
- mod_label = {"PORTRAIT_MODE": "πŸ“± Portrait Mode", "MESSAGING": "πŸ’¬ Messaging App",
435
- "SMARTPHONE": "πŸ“± Smartphone", "SCREENSHOT": "πŸ–₯οΏ½οΏ½ Screenshot",
436
- "DSLR": "πŸ“· DSLR", "UNKNOWN": "❓ Unknown"}.get(mod_name, mod_name)
 
 
 
 
 
 
 
 
 
437
 
438
  verdict_html = f"""
439
  <div style="background:{bg}; color:white; padding:24px; border-radius:16px;
@@ -442,7 +475,7 @@ def analyze_image(img):
442
  <div style="font-size:28px; font-weight:700; margin-bottom:4px;">{verdict.verdict}</div>
443
  <div style="font-size:42px; font-weight:800;">{prob:.1%}</div>
444
  <div style="font-size:14px; opacity:0.9; margin-top:4px;">
445
- Confidence: {verdict.confidence} | Agents: {len([a for a in agent_results if a.failure_prob < 0.8])}/7 active
446
  </div>
447
  <div style="font-size:12px; opacity:0.7; margin-top:6px; border-top:1px solid rgba(255,255,255,0.2); padding-top:6px;">
448
  Modality: {mod_label} ({mod_conf:.0%}) | {mod_adj} test(s) recalibrated
@@ -476,6 +509,8 @@ def analyze_image(img):
476
  _build_agent_details_md(agent_results),
477
  )
478
  except Exception as e:
 
 
479
  error_html = f"""
480
  <div style="background:linear-gradient(135deg, #6c757d, #495057); color:white;
481
  padding:24px; border-radius:16px; text-align:center;">
@@ -518,7 +553,12 @@ def _build_agent_details_md(agent_results: List[AgentEvidence]) -> str:
518
  note = sf.get("note", "")
519
  s = sf.get("score", 0)
520
  ic = "πŸ”΄" if s > 0.2 else "🟒" if s < -0.1 else "🟑"
521
- md += f"- {ic} **{test}** ({s:+.2f}): {note}\n"
 
 
 
 
 
522
  md += "\n---\n\n"
523
  return md
524
 
 
50
  from agents.modality_detector import detect_modality
51
  modality = detect_modality(img)
52
  adj = modality.score_adjustments
53
+
54
+ print(f"[FORENSIQ] Modality: {modality.modality} (conf={modality.confidence})", file=sys.stderr)
55
+ print(f"[FORENSIQ] Adjustments: {len(adj)} tests recalibrated", file=sys.stderr)
56
 
57
  # Signal processing agents (fast, run in parallel with modality adjustments)
58
  signal_agents = [
 
81
  try:
82
  results[name] = future.result()
83
  except Exception as e:
84
+ print(f"[FORENSIQ] Agent '{name}' FAILED: {e}", file=sys.stderr)
85
  results[name] = AgentEvidence(
86
  agent_name=f"{name.title()} Agent (Error)",
87
  violation_score=0.0,
 
101
  results.get("text"),
102
  ]
103
  ordered = [r for r in ordered if r is not None]
104
+
105
+ # Log agent scores for debugging
106
+ for a in ordered:
107
+ status = "ACTIVE" if a.failure_prob < 0.8 else "FAILED"
108
+ print(f"[FORENSIQ] {a.agent_name}: score={a.violation_score:+.3f} conf={a.confidence:.3f} fail={a.failure_prob:.2f} [{status}]", file=sys.stderr)
109
+
110
+ # FIX Bug 3: Filter out failed agents BEFORE Bayesian synthesis
111
+ # Failed agents (failure_prob >= 0.8) contribute no evidence β€” they're
112
+ # ghost entries that shouldn't appear in the reasoning tree or affect
113
+ # the active agent count display.
114
+ active_agents = [r for r in ordered if r.failure_prob < 0.8]
115
+ failed_agents = [r for r in ordered if r.failure_prob >= 0.8]
116
+
117
+ n_active = len(active_agents)
118
+ n_total = len(ordered)
119
+ print(f"[FORENSIQ] Active agents: {n_active}/{n_total}", file=sys.stderr)
120
+
121
+ # Bayesian synthesis β€” only pass active agents
122
+ verdict = bayesian_synthesis(active_agents)
123
 
124
  # Attach modality info to verdict for reporting β€” include ALL indicators for diagnostics
125
  verdict.reasoning_tree["modality"] = {
 
129
  if not isinstance(v, np.ndarray) and k != "modality_scores"},
130
  "modality_scores": modality.indicators.get("modality_scores", {}),
131
  "adjustments_applied": len(modality.score_adjustments),
132
+ "adjustments_list": list(modality.score_adjustments.keys())[:10],
133
  }
134
+
135
+ # Track failed agents in the tree for transparency
136
+ if failed_agents:
137
+ verdict.reasoning_tree["failed_agents"] = [
138
+ {"name": a.agent_name, "reason": a.rationale[:200]}
139
+ for a in failed_agents
140
+ ]
141
 
142
  # Generate explanations
143
  verdict.forensic_report = generate_forensic_report(verdict)
144
  reasoning = generate_reasoning_tree(verdict)
145
  verdict.court_brief = generate_court_brief(verdict)
146
 
147
+ # Return ALL agents (including failed) for display, but verdict uses only active
148
  return ordered, verdict, reasoning
149
 
150
 
 
202
  for agent in agent_results:
203
  short_name = agent.agent_name.replace(" Agent", "").replace(" Characteristics", "")
204
  names.append(short_name)
 
205
  display_score = (agent.violation_score + 1) * 50
206
  scores.append(display_score)
207
  if agent.violation_score > 0.2:
 
211
  else:
212
  colors.append("gold")
213
 
 
214
  names_closed = names + [names[0]]
215
  scores_closed = scores + [scores[0]]
216
 
 
225
  name="Violation Score",
226
  ))
227
 
 
228
  fig.add_trace(go.Scatterpolar(
229
  r=[50] * (len(names) + 1),
230
  theta=names_closed,
 
322
  yaxis=dict(showticklabels=False, scaleanchor="x"),
323
  )
324
  return fig
 
325
  fig = go.Figure()
326
  fig.update_layout(height=400, title="FFT Spectrum (not available)")
327
  return fig
 
455
  mod_name = mod_info.get("detected", "UNKNOWN")
456
  mod_conf = mod_info.get("confidence", 0)
457
  mod_adj = mod_info.get("adjustments_applied", 0)
458
+ mod_label = {
459
+ "PORTRAIT_MODE": "πŸ“± Portrait Mode",
460
+ "MESSAGING": "πŸ’¬ Messaging App",
461
+ "SMARTPHONE": "πŸ“± Smartphone",
462
+ "SCREENSHOT": "πŸ–₯️ Screenshot",
463
+ "DSLR": "πŸ“· DSLR",
464
+ "MACRO_DSLR": "πŸ”¬ Macro DSLR",
465
+ "UNKNOWN": "❓ Unknown",
466
+ }.get(mod_name, f"πŸ“· {mod_name}")
467
+
468
+ # Count active agents from verdict (which only has active agents now)
469
+ n_active = len(verdict.agent_results)
470
 
471
  verdict_html = f"""
472
  <div style="background:{bg}; color:white; padding:24px; border-radius:16px;
 
475
  <div style="font-size:28px; font-weight:700; margin-bottom:4px;">{verdict.verdict}</div>
476
  <div style="font-size:42px; font-weight:800;">{prob:.1%}</div>
477
  <div style="font-size:14px; opacity:0.9; margin-top:4px;">
478
+ Confidence: {verdict.confidence} | Agents: {n_active}/7 active
479
  </div>
480
  <div style="font-size:12px; opacity:0.7; margin-top:6px; border-top:1px solid rgba(255,255,255,0.2); padding-top:6px;">
481
  Modality: {mod_label} ({mod_conf:.0%}) | {mod_adj} test(s) recalibrated
 
509
  _build_agent_details_md(agent_results),
510
  )
511
  except Exception as e:
512
+ import traceback
513
+ traceback.print_exc(file=sys.stderr)
514
  error_html = f"""
515
  <div style="background:linear-gradient(135deg, #6c757d, #495057); color:white;
516
  padding:24px; border-radius:16px; text-align:center;">
 
553
  note = sf.get("note", "")
554
  s = sf.get("score", 0)
555
  ic = "πŸ”΄" if s > 0.2 else "🟒" if s < -0.1 else "🟑"
556
+ # Show modality adjustment info
557
+ if sf.get("modality_adjusted"):
558
+ adj_info = f" [Γ—{sf.get('adjustment_multiplier', '?')} modality]"
559
+ else:
560
+ adj_info = ""
561
+ md += f"- {ic} **{test}** ({s:+.2f}{adj_info}): {note}\n"
562
  md += "\n---\n\n"
563
  return md
564