lsdf commited on
Commit
42ad6d7
·
1 Parent(s): eb8813a

Add detailed optimizer debug trace for candidate-level visibility

Browse files

Expose per-iteration sentence edits, candidate scores, validity flags, rejection reasons, and chosen variants in UI to diagnose why optimization steps are accepted or rejected.

Made-with: Cursor

Files changed (2) hide show
  1. optimizer.py +34 -0
  2. templates/index.html +58 -0
optimizer.py CHANGED
@@ -505,6 +505,7 @@ def optimize_text(request_data: Dict[str, Any]) -> Dict[str, Any]:
505
  "goal_improved": goal_improved,
506
  "invalid_reasons": invalid_reasons,
507
  "delta_score": delta_score,
 
508
  }
509
  )
510
  except Exception as e:
@@ -516,6 +517,7 @@ def optimize_text(request_data: Dict[str, Any]) -> Dict[str, Any]:
516
  "goal_improved": False,
517
  "invalid_reasons": [str(e)],
518
  "delta_score": -999.0,
 
519
  }
520
  )
521
 
@@ -535,6 +537,8 @@ def optimize_text(request_data: Dict[str, Any]) -> Dict[str, Any]:
535
  "goal_improved": c.get("goal_improved", False),
536
  "invalid_reasons": c.get("invalid_reasons", []),
537
  "delta_score": c.get("delta_score"),
 
 
538
  "error": c.get("error"),
539
  }
540
  for c in candidates
@@ -559,6 +563,22 @@ def optimize_text(request_data: Dict[str, Any]) -> Dict[str, Any]:
559
  "reason": "Best valid candidate did not improve total score.",
560
  "best_candidate_score": best["metrics"]["score"],
561
  "current_score": current_metrics["score"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  }
563
  )
564
  continue
@@ -580,6 +600,20 @@ def optimize_text(request_data: Dict[str, Any]) -> Dict[str, Any]:
580
  "metrics_before": prev_metrics,
581
  "metrics_after": current_metrics,
582
  "delta_score": round(current_metrics["score"] - prev_metrics["score"], 3),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
583
  }
584
  )
585
 
 
505
  "goal_improved": goal_improved,
506
  "invalid_reasons": invalid_reasons,
507
  "delta_score": delta_score,
508
+ "candidate_score": cand_metrics.get("score"),
509
  }
510
  )
511
  except Exception as e:
 
517
  "goal_improved": False,
518
  "invalid_reasons": [str(e)],
519
  "delta_score": -999.0,
520
+ "candidate_score": None,
521
  }
522
  )
523
 
 
537
  "goal_improved": c.get("goal_improved", False),
538
  "invalid_reasons": c.get("invalid_reasons", []),
539
  "delta_score": c.get("delta_score"),
540
+ "candidate_score": c.get("candidate_score"),
541
+ "sentence_after": c.get("sentence_after"),
542
  "error": c.get("error"),
543
  }
544
  for c in candidates
 
563
  "reason": "Best valid candidate did not improve total score.",
564
  "best_candidate_score": best["metrics"]["score"],
565
  "current_score": current_metrics["score"],
566
+ "best_candidate_index": best.get("candidate_index"),
567
+ "best_candidate_goal_improved": best.get("goal_improved", False),
568
+ "best_candidate_sentence_after": best.get("sentence_after"),
569
+ "candidates": [
570
+ {
571
+ "candidate_index": c.get("candidate_index"),
572
+ "valid": c.get("valid", False),
573
+ "goal_improved": c.get("goal_improved", False),
574
+ "invalid_reasons": c.get("invalid_reasons", []),
575
+ "delta_score": c.get("delta_score"),
576
+ "candidate_score": c.get("candidate_score"),
577
+ "sentence_after": c.get("sentence_after"),
578
+ "error": c.get("error"),
579
+ }
580
+ for c in candidates
581
+ ],
582
  }
583
  )
584
  continue
 
600
  "metrics_before": prev_metrics,
601
  "metrics_after": current_metrics,
602
  "delta_score": round(current_metrics["score"] - prev_metrics["score"], 3),
603
+ "chosen_candidate_index": best.get("candidate_index"),
604
+ "candidates": [
605
+ {
606
+ "candidate_index": c.get("candidate_index"),
607
+ "valid": c.get("valid", False),
608
+ "goal_improved": c.get("goal_improved", False),
609
+ "invalid_reasons": c.get("invalid_reasons", []),
610
+ "delta_score": c.get("delta_score"),
611
+ "candidate_score": c.get("candidate_score"),
612
+ "sentence_after": c.get("sentence_after"),
613
+ "error": c.get("error"),
614
+ }
615
+ for c in candidates
616
+ ],
617
  }
618
  )
619
 
templates/index.html CHANGED
@@ -825,6 +825,13 @@
825
  ['Semantic gaps', base.semantic_gap_count, fin.semantic_gap_count],
826
  ].map(r => `<tr><td>${r[0]}</td><td>${r[1]}</td><td>${r[2]}</td></tr>`).join('');
827
 
 
 
 
 
 
 
 
828
  const iterRows = (data.iterations || []).map(it => {
829
  const before = it.metrics_before ? it.metrics_before.score : '-';
830
  const after = it.metrics_after ? it.metrics_after.score : '-';
@@ -840,6 +847,53 @@
840
  </tr>`;
841
  }).join('');
842
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843
  container.innerHTML = `
844
  <div class="stat-card">
845
  <h6 class="card-title">Результат оптимизации</h6>
@@ -860,6 +914,10 @@
860
  <tbody>${iterRows || '<tr><td colspan="7" class="text-muted text-center">Нет данных</td></tr>'}</tbody>
861
  </table>
862
  </div>
 
 
 
 
863
  </div>`;
864
  }
865
 
 
825
  ['Semantic gaps', base.semantic_gap_count, fin.semantic_gap_count],
826
  ].map(r => `<tr><td>${r[0]}</td><td>${r[1]}</td><td>${r[2]}</td></tr>`).join('');
827
 
828
+ const safeHtml = (v) => String(v ?? '')
829
+ .replace(/&/g, '&amp;')
830
+ .replace(/</g, '&lt;')
831
+ .replace(/>/g, '&gt;')
832
+ .replace(/"/g, '&quot;')
833
+ .replace(/'/g, '&#39;');
834
+
835
  const iterRows = (data.iterations || []).map(it => {
836
  const before = it.metrics_before ? it.metrics_before.score : '-';
837
  const after = it.metrics_after ? it.metrics_after.score : '-';
 
847
  </tr>`;
848
  }).join('');
849
 
850
+ const iterationDebugHtml = (data.iterations || []).map(it => {
851
+ const candidates = Array.isArray(it.candidates) ? it.candidates : [];
852
+ const candidateRows = candidates.map(c => {
853
+ const reasons = Array.isArray(c.invalid_reasons) ? c.invalid_reasons.join(', ') : '';
854
+ const sentAfter = c.sentence_after ? safeHtml(c.sentence_after) : '-';
855
+ return `
856
+ <tr>
857
+ <td>${c.candidate_index ?? '-'}</td>
858
+ <td>${c.valid ? 'yes' : 'no'}</td>
859
+ <td>${c.goal_improved ? 'yes' : 'no'}</td>
860
+ <td>${c.delta_score ?? '-'}</td>
861
+ <td>${c.candidate_score ?? '-'}</td>
862
+ <td>${safeHtml(reasons || c.error || '-')}</td>
863
+ <td><div style="max-width: 520px; white-space: normal;">${sentAfter}</div></td>
864
+ </tr>
865
+ `;
866
+ }).join('');
867
+
868
+ const sentBefore = it.sentence_before ? safeHtml(it.sentence_before) : '-';
869
+ const sentAfterChosen = it.sentence_after
870
+ ? safeHtml(it.sentence_after)
871
+ : (it.best_candidate_sentence_after ? safeHtml(it.best_candidate_sentence_after) : '-');
872
+
873
+ return `
874
+ <div class="card mb-3 border-0 shadow-sm">
875
+ <div class="card-body">
876
+ <div class="d-flex justify-content-between align-items-center mb-2">
877
+ <div><strong>Шаг ${it.step}</strong> — ${safeHtml(it.status || '-')}</div>
878
+ <span class="badge bg-secondary">${safeHtml(it.goal ? (it.goal.type + ': ' + (it.goal.label || '')) : '-')}</span>
879
+ </div>
880
+ <div class="small mb-2"><strong>Причина:</strong> ${safeHtml(it.reason || '-')}</div>
881
+ <div class="small mb-2"><strong>Исходное предложение:</strong><br><span class="text-muted">${sentBefore}</span></div>
882
+ <div class="small mb-2"><strong>Выбранный вариант:</strong><br><span class="text-muted">${sentAfterChosen}</span></div>
883
+ <div class="table-responsive">
884
+ <table class="table table-sm table-bordered mb-0">
885
+ <thead class="table-light">
886
+ <tr>
887
+ <th>#cand</th><th>valid</th><th>goal+</th><th>Δ</th><th>score</th><th>reject reason/error</th><th>кандидат правки</th>
888
+ </tr>
889
+ </thead>
890
+ <tbody>${candidateRows || '<tr><td colspan="7" class="text-center text-muted">Нет кандидатов</td></tr>'}</tbody>
891
+ </table>
892
+ </div>
893
+ </div>
894
+ </div>`;
895
+ }).join('');
896
+
897
  container.innerHTML = `
898
  <div class="stat-card">
899
  <h6 class="card-title">Результат оптимизации</h6>
 
914
  <tbody>${iterRows || '<tr><td colspan="7" class="text-muted text-center">Нет данных</td></tr>'}</tbody>
915
  </table>
916
  </div>
917
+ </div>
918
+ <div class="stat-card">
919
+ <h6 class="card-title">Подробный debug-лог итераций</h6>
920
+ ${iterationDebugHtml || '<div class="text-muted">Нет данных.</div>'}
921
  </div>`;
922
  }
923