Spaces:
Running
Running
Add detailed optimizer debug trace for candidate-level visibility
Browse filesExpose 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
- optimizer.py +34 -0
- 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, '&')
|
| 830 |
+
.replace(/</g, '<')
|
| 831 |
+
.replace(/>/g, '>')
|
| 832 |
+
.replace(/"/g, '"')
|
| 833 |
+
.replace(/'/g, ''');
|
| 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 |
|