QoL and metric value inverter

This commit is contained in:
2026-03-04 11:10:45 -06:00
parent 8dfe3607b1
commit f57ac7d6dc
30 changed files with 556 additions and 118 deletions

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
import math
import os
import secrets
from pathlib import Path
@@ -53,10 +54,54 @@ def close_db(exc: BaseException | None = None) -> None:
repo.conn.close()
_SI_PREFIXES = [
(1e12, "T"),
(1e9, "G"),
(1e6, "M"),
(1e3, "k"),
]
def _si_format(value: object) -> str:
"""Format a number with SI prefixes for readability.
Handles string inputs (like dep.value) by trying float conversion first.
Non-numeric values are returned as-is.
"""
if isinstance(value, str):
try:
num = float(value)
except (ValueError, TypeError):
return value
elif isinstance(value, (int, float)):
num = float(value)
else:
return str(value)
if math.isnan(num) or math.isinf(num):
return str(value)
abs_num = abs(num)
if abs_num < 1000:
# Small numbers: drop trailing zeros, cap at 4 significant figures
if num == int(num) and abs_num < 100:
return str(int(num))
return f"{num:.4g}"
for threshold, prefix in _SI_PREFIXES:
if abs_num >= threshold:
scaled = num / threshold
return f"{scaled:.4g}{prefix}"
return f"{num:.4g}"
def create_app() -> Flask:
app = Flask(__name__)
app.secret_key = _load_or_generate_secret_key()
app.jinja_env.filters["si"] = _si_format
app.teardown_appcontext(close_db)
# Register blueprints

View File

@@ -80,7 +80,11 @@ def metric_add(domain_id: int):
if not metric_name:
flash("Metric name is required.", "error")
else:
mb = MetricBound(metric_name=metric_name, weight=weight, norm_min=norm_min, norm_max=norm_max, unit=unit)
lower_is_better = bool(request.form.get("lower_is_better"))
mb = MetricBound(
metric_name=metric_name, weight=weight, norm_min=norm_min,
norm_max=norm_max, unit=unit, lower_is_better=lower_is_better,
)
repo.add_metric_bound(domain_id, mb)
flash("Metric added.", "success")
domain = repo.get_domain_by_id(domain_id)
@@ -99,7 +103,8 @@ def metric_edit(domain_id: int, metric_id: int):
domain = repo.get_domain_by_id(domain_id)
return render_template("domains/_metrics_table.html", domain=domain)
unit = request.form.get("unit", "").strip()
repo.update_metric_bound(domain_id, metric_id, weight, norm_min, norm_max, unit)
lower_is_better = bool(request.form.get("lower_is_better"))
repo.update_metric_bound(domain_id, metric_id, weight, norm_min, norm_max, unit, lower_is_better)
flash("Metric updated.", "success")
domain = repo.get_domain_by_id(domain_id)
return render_template("domains/_metrics_table.html", domain=domain)

View File

@@ -54,6 +54,9 @@ def result_detail(domain_name: str, combo_id: int):
return redirect(url_for("results.results_domain", domain_name=domain_name))
result = repo.get_result(combo_id, domain.id)
if not result:
flash("No results for this combination in this domain.", "error")
return redirect(url_for("results.results_domain", domain_name=domain_name))
scores = repo.get_combination_scores(combo_id, domain.id)
return render_template(

View File

@@ -54,6 +54,7 @@ h3 { font-size: 1rem; margin-bottom: 0.25rem; }
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1rem;
}
.card-grid > * { min-width: 0; overflow-x: auto; }
/* ── Tables ──────────────────────────────────────────────── */
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
@@ -79,7 +80,10 @@ table.compact th, table.compact td { padding: 0.25rem 0.4rem; font-size: 0.85rem
.badge-range_min, .badge-range_max { background: #fef3c7; color: #92400e; }
.badge-excludes { background: #fee2e2; color: #991b1b; }
.badge-valid { background: #dcfce7; color: #166534; }
.badge-blocked { background: #fee2e2; color: #991b1b; }
.badge-p1_fail { background: #fee2e2; color: #991b1b; }
.badge-p2_fail { background: #fee2e2; color: #991b1b; }
.badge-p3_fail { background: #fee2e2; color: #991b1b; }
.badge-p4_fail { background: #fee2e2; color: #991b1b; }
.badge-scored { background: #dbeafe; color: #1e40af; }
.badge-llm_reviewed { background: #e0f2fe; color: #0369a1; }
.badge-reviewed { background: #f3e8ff; color: #6b21a8; }

View File

@@ -6,6 +6,7 @@
<th>Weight</th>
<th>Norm Min</th>
<th>Norm Max</th>
<th>Direction</th>
<th></th>
</tr>
</thead>
@@ -15,8 +16,9 @@
<td>{{ mb.metric_name }}</td>
<td>{{ mb.unit or '—' }}</td>
<td>{{ mb.weight }}</td>
<td>{{ mb.norm_min }}</td>
<td>{{ mb.norm_max }}</td>
<td>{{ mb.norm_min|si }}</td>
<td>{{ mb.norm_max|si }}</td>
<td>{{ '↓ lower' if mb.lower_is_better else '↑ higher' }}</td>
<td class="actions">
<button class="btn btn-sm"
onclick="this.closest('tr').nextElementSibling.style.display='table-row'; this.closest('tr').style.display='none'">
@@ -39,6 +41,7 @@
<td><input name="weight" type="number" step="any" value="{{ mb.weight }}" required></td>
<td><input name="norm_min" type="number" step="any" value="{{ mb.norm_min }}" required></td>
<td><input name="norm_max" type="number" step="any" value="{{ mb.norm_max }}" required></td>
<td><label><input type="checkbox" name="lower_is_better" value="1" {{ 'checked' if mb.lower_is_better }}> lower is better</label></td>
<td>
<button type="submit" class="btn btn-sm btn-primary">Save</button>
<button type="button" class="btn btn-sm"
@@ -63,6 +66,7 @@
<input name="weight" type="number" step="any" placeholder="weight" value="1.0" required>
<input name="norm_min" type="number" step="any" placeholder="norm min" value="0.0" required>
<input name="norm_max" type="number" step="any" placeholder="norm max" value="1.0" required>
<label><input type="checkbox" name="lower_is_better" value="1"> lower is better</label>
<button type="submit" class="btn btn-primary">Add</button>
</div>
</form>

View File

@@ -16,7 +16,7 @@
<p>{{ d.description }}</p>
<table>
<thead>
<tr><th>Metric</th><th>Unit</th><th>Weight</th><th>Norm Min</th><th>Norm Max</th></tr>
<tr><th>Metric</th><th>Unit</th><th>Weight</th><th>Norm Min</th><th>Norm Max</th><th>Direction</th></tr>
</thead>
<tbody>
{% for mb in d.metric_bounds %}
@@ -24,8 +24,9 @@
<td>{{ mb.metric_name }}</td>
<td>{{ mb.unit }}</td>
<td>{{ mb.weight }}</td>
<td>{{ mb.norm_min }}</td>
<td>{{ mb.norm_max }}</td>
<td>{{ mb.norm_min|si }}</td>
<td>{{ mb.norm_max|si }}</td>
<td>{{ '↓ lower' if mb.lower_is_better else '↑ higher' }}</td>
</tr>
{% endfor %}
</tbody>

View File

@@ -14,7 +14,7 @@
<tr>
<td>{{ dep.category }}</td>
<td>{{ dep.key }}</td>
<td>{{ dep.value }}</td>
<td>{{ dep.value|si }}</td>
<td>{{ dep.unit or '—' }}</td>
<td><span class="badge badge-{{ dep.constraint_type }}">{{ dep.constraint_type }}</span></td>
<td class="actions">

View File

@@ -34,7 +34,7 @@
<td>1 — Constraints</td>
<td>{{ run.combos_pass1 or 0 }} checked
{%- if (run.combos_pass2 or 0) > 0 and (run.combos_pass1 or 0) > (run.combos_pass2 or 0) %},
<span class="badge badge-blocked">{{ (run.combos_pass1 or 0) - (run.combos_pass2 or 0) }} blocked</span>
<span class="badge badge-p1_fail">{{ (run.combos_pass1 or 0) - (run.combos_pass2 or 0) }} failed</span>
{%- endif -%}
</td>
</tr>

View File

@@ -97,7 +97,7 @@
<th>Status</th>
<th>Total</th>
<th>P1 Checked</th>
<th>P1 Blocked</th>
<th>P1 Failed</th>
<th>P2 Estimated</th>
<th>P3 Scored</th>
<th>P4 Reviewed</th>
@@ -113,7 +113,7 @@
<td><span class="badge badge-{{ run.status }}">{{ run.status }}</span></td>
<td>{{ run.total_combos or '—' }}</td>
<td>{{ run.combos_pass1 or '—' }}</td>
<td>{% if blocked %}<span class="badge badge-blocked">{{ blocked }}</span>{% else %}—{% endif %}</td>
<td>{% if blocked %}<span class="badge badge-p1_fail">{{ blocked }}</span>{% else %}—{% endif %}</td>
<td>{{ run.combos_pass2 or '—' }}</td>
<td>{{ run.combos_pass3 or '—' }}</td>
<td>{{ run.combos_pass4 or '—' }}</td>
@@ -133,7 +133,7 @@
<h3>{{ d.name }} <span class="subtitle">{{ d.description }}</span></h3>
<dl class="summary-dl">
<dt>Results</dt><dd>{{ s.total_results }} scored combinations</dd>
<dt>Blocked</dt><dd>{{ s.blocked }} combinations</dd>
<dt>Failed</dt><dd>{{ s.failed }} combinations</dd>
<dt>Score range</dt><dd class="score-cell">{{ "%.4f"|format(s.min_score) }} — {{ "%.4f"|format(s.max_score) }}</dd>
<dt>Avg score</dt><dd class="score-cell">{{ "%.4f"|format(s.avg_score) }}</dd>
<dt>Last pass</dt><dd>{{ s.last_pass }}</dd>

View File

@@ -43,7 +43,7 @@
{% for dep in e.dependencies %}
<tr>
<td>{{ dep.key }}</td>
<td>{{ dep.value }}{{ ' ' + dep.unit if dep.unit else '' }}</td>
<td>{{ dep.value|si }}{{ ' ' + dep.unit if dep.unit else '' }}</td>
<td><span class="badge badge-{{ dep.constraint_type }}">{{ dep.constraint_type }}</span></td>
</tr>
{% endfor %}
@@ -77,10 +77,10 @@
<tr>
<td>{{ s.metric_name }}</td>
{% set unit = s.metric_unit or '' %}
<td class="score-cell">{{ "%.2f"|format(s.raw_value) if s.raw_value is not none else '—' }}{{ ' ' + unit if unit and s.raw_value is not none else '' }}</td>
<td class="score-cell">{{ s.raw_value|si if s.raw_value is not none else '—' }}{{ ' ' + unit if unit and s.raw_value is not none else '' }}</td>
<td>
{%- if mb -%}
{{ "%.2f"|format(mb.norm_min) }} — {{ "%.2f"|format(mb.norm_max) }}{{ ' ' + unit if unit else '' }}
{{ mb.norm_min|si }} — {{ mb.norm_max|si }}{{ ' ' + unit if unit else '' }}
{%- else -%}
{%- endif -%}
@@ -88,22 +88,22 @@
<td>
{%- if mb and s.raw_value is not none -%}
{%- if s.raw_value <= mb.norm_min -%}
<span class="badge badge-blocked">at/below min</span>
<span class="badge badge-{{ 'valid' if mb.lower_is_better else 'p1_fail' }}">at/below min{{ ' (best)' if mb.lower_is_better else '' }}</span>
{%- elif s.raw_value >= mb.norm_max -%}
<span class="badge badge-valid">at/above max</span>
<span class="badge badge-{{ 'p1_fail' if mb.lower_is_better else 'valid' }}">at/above max{{ ' (worst)' if mb.lower_is_better else '' }}</span>
{%- else -%}
{% set pct = ((s.raw_value - mb.norm_min) / (mb.norm_max - mb.norm_min) * 100) | int %}
<div class="metric-bar-container">
<div class="metric-bar" style="width: {{ pct }}%"></div>
</div>
<span class="metric-bar-label">~{{ pct }}%</span>
<span class="metric-bar-label">~{{ pct }}%{{ ' ↓' if mb.lower_is_better else '' }}</span>
{%- endif -%}
{%- else -%}
{%- endif -%}
</td>
<td class="score-cell">{{ "%.4f"|format(s.normalized_score) if s.normalized_score is not none else '—' }}</td>
<td>{{ "%.0f%%"|format(mb.weight * 100) if mb else '—' }}</td>
<td>{{ "%.0f%%"|format(mb.weight * 100) if mb else '—' }}{{ ' ↓' if mb and mb.lower_is_better else '' }}</td>
</tr>
{% endfor %}
</tbody>

View File

@@ -64,7 +64,7 @@
<td>{{ r.combination.entities|map(attribute='name')|join(' + ') }}</td>
<td><span class="badge badge-{{ r.combination.status }}">{{ r.combination.status }}</span></td>
<td class="block-reason-cell">
{%- if r.combination.status == 'blocked' and r.combination.block_reason -%}
{%- if r.combination.status.endswith('_fail') and r.combination.block_reason -%}
{{ r.combination.block_reason }}
{%- elif r.novelty_flag -%}
{{ r.novelty_flag }}