new changes in the api
This commit is contained in:
@@ -452,43 +452,6 @@
|
||||
transform: scaleY(1.1)
|
||||
}
|
||||
|
||||
/* ── STRATEGY SWITCHER ── */
|
||||
.strategy-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: .5rem
|
||||
}
|
||||
|
||||
.strategy-card {
|
||||
padding: .75rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
}
|
||||
|
||||
.strategy-card:hover {
|
||||
border-color: var(--border2)
|
||||
}
|
||||
|
||||
.strategy-card.active {
|
||||
border-color: var(--accent);
|
||||
background: rgba(0, 212, 255, .06)
|
||||
}
|
||||
|
||||
.strategy-name {
|
||||
font-size: .75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: .05em;
|
||||
margin-bottom: .2rem
|
||||
}
|
||||
|
||||
.strategy-desc {
|
||||
font-family: var(--mono);
|
||||
font-size: .62rem;
|
||||
color: var(--muted)
|
||||
}
|
||||
|
||||
/* ── BADGE ── */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
@@ -718,11 +681,6 @@
|
||||
<div class="stat-value v-accent" id="sMLParams">—</div>
|
||||
<div class="stat-sub" id="sParamsSub">—</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">SLA Breaches</div>
|
||||
<div class="stat-value" id="sSLA">—</div>
|
||||
<div class="stat-sub">Recent window</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Avg Latency</div>
|
||||
<div class="stat-value" id="sLatency">—</div>
|
||||
@@ -758,17 +716,8 @@
|
||||
<div class="chart-wrap"><canvas id="importanceChart"></canvas></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="panel-title">
|
||||
Strategy Mode
|
||||
<span class="badge badge-purple" id="activeStrategyBadge">—</span>
|
||||
</div>
|
||||
<div class="strategy-grid" id="strategyGrid">
|
||||
<!-- injected -->
|
||||
</div>
|
||||
<div style="margin-top:1rem">
|
||||
<div class="panel-title" style="margin-bottom:.5rem">Multi-Objective Pareto</div>
|
||||
<div class="chart-wrap" style="height:120px"><canvas id="paretoChart"></canvas></div>
|
||||
</div>
|
||||
<div class="panel-title">Multi-Objective Pareto</div>
|
||||
<div class="chart-wrap" style="height:120px"><canvas id="paretoChart"></canvas></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="panel-title">
|
||||
@@ -794,28 +743,6 @@
|
||||
<div class="panel-title">Quality Distribution</div>
|
||||
<div class="chart-wrap"><canvas id="histogramChart"></canvas></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="panel-title">Strategy Comparison</div>
|
||||
<div class="tbl-scroll">
|
||||
<table class="ml-table" id="strategyTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Strategy</th>
|
||||
<th>Calls</th>
|
||||
<th>Avg Q</th>
|
||||
<th>Unassigned</th>
|
||||
<th>Avg km</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="strategyTableBody">
|
||||
<tr>
|
||||
<td colspan="5" style="color:var(--muted);text-align:center;padding:1.5rem">Loading...
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ROW 4: Zone stats + Top trials + Model health -->
|
||||
@@ -830,13 +757,12 @@
|
||||
<th>Zone</th>
|
||||
<th>Calls</th>
|
||||
<th>Avg Q</th>
|
||||
<th>SLA Breaches</th>
|
||||
<th>Avg km</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="zoneTableBody">
|
||||
<tr>
|
||||
<td colspan="5" style="color:var(--muted);text-align:center;padding:1.5rem">Loading...
|
||||
<td colspan="4" style="color:var(--muted);text-align:center;padding:1.5rem">Loading...
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -900,14 +826,6 @@
|
||||
<script>
|
||||
const API = '/api/v1/ml';
|
||||
let charts = {};
|
||||
let activeStrategy = 'balanced';
|
||||
|
||||
const STRATEGIES = {
|
||||
aggressive_speed: { name: 'Aggressive Speed', desc: 'Max completions, ignore balance', color: '#ff4466' },
|
||||
fuel_saver: { name: 'Fuel Saver', desc: 'Minimize total route distance', color: '#00e57a' },
|
||||
zone_strict: { name: 'Zone Strict', desc: 'Balance + local routes', color: '#7c3aed' },
|
||||
balanced: { name: 'Balanced', desc: 'Even spread across all metrics', color: '#00d4ff' },
|
||||
};
|
||||
|
||||
const PARAM_DESC = {
|
||||
max_pickup_distance_km: 'Max distance a rider can be from kitchen for initial pickup',
|
||||
@@ -951,10 +869,8 @@
|
||||
renderLatencyChart(data.quality_trend || {});
|
||||
renderModelHealth(data.model || {});
|
||||
renderBehaviorRules(data.behavior || {});
|
||||
renderStrategyGrid(data.config?.ml_strategy || 'balanced');
|
||||
renderHeatmap(data.hourly_stats || []);
|
||||
renderHistogram(data.quality_histogram || []);
|
||||
renderStrategyTable(data.strategy_comparison || []);
|
||||
renderZoneTable(data.zone_stats || []);
|
||||
renderTrialsTable(data.model?.top_trials || []);
|
||||
renderImportanceChart(data.model?.feature_importance || {});
|
||||
@@ -1007,11 +923,6 @@
|
||||
const latArr = trend.latency_series || [];
|
||||
const avgLat = latArr.length ? (latArr.reduce((a, b) => a + b, 0) / latArr.length).toFixed(0) : '—';
|
||||
set('sLatency', avgLat !== '—' ? avgLat + 'ms' : '—');
|
||||
|
||||
// SLA count from hourly stats
|
||||
const slaCount = (data.hourly_stats || []).reduce((s, r) => s + (r.sla_breaches || 0), 0);
|
||||
set('sSLA', slaCount);
|
||||
el('sSLA').className = 'stat-value ' + (slaCount === 0 ? 'v-green' : slaCount < 5 ? 'v-amber' : 'v-red');
|
||||
}
|
||||
|
||||
// ─────────────────────────── CONFIG ───────────────────────────
|
||||
@@ -1271,7 +1182,7 @@
|
||||
<div style="font-family:var(--mono);font-size:.55rem;color:rgba(255,255,255,.5)">${h}h</div>
|
||||
<div style="font-family:var(--mono);font-size:.6rem;color:#fff;font-weight:500">${q ? q.toFixed(0) : '—'}</div>
|
||||
`;
|
||||
cell.title = `Hour ${h}: Q=${q.toFixed(1)}, SLA breaches=${d?.sla_breaches || 0}, calls=${d?.call_count || 0}`;
|
||||
cell.title = `Hour ${h}: Q=${q.toFixed(1)}, calls=${d?.call_count || 0}`;
|
||||
row.appendChild(cell);
|
||||
}
|
||||
container.appendChild(row);
|
||||
@@ -1285,28 +1196,14 @@
|
||||
}
|
||||
|
||||
// ─────────────────────────── TABLES ───────────────────────────
|
||||
function renderStrategyTable(rows) {
|
||||
const tbody = el('strategyTableBody');
|
||||
if (!rows.length) { tbody.innerHTML = '<tr><td colspan="5" style="color:var(--muted);text-align:center;padding:1.5rem">No data yet</td></tr>'; return; }
|
||||
tbody.innerHTML = rows.map(r => `
|
||||
<tr>
|
||||
<td><span class="badge badge-blue">${r.strategy}</span></td>
|
||||
<td>${r.call_count}</td>
|
||||
<td><span class="${r.avg_quality > 75 ? 'badge-green' : r.avg_quality > 50 ? 'badge-amber' : 'badge-red'} badge">${r.avg_quality}%</span></td>
|
||||
<td>${r.avg_unassigned.toFixed(1)}</td>
|
||||
<td>${r.avg_distance_km.toFixed(1)} km</td>
|
||||
</tr>`).join('');
|
||||
}
|
||||
|
||||
function renderZoneTable(rows) {
|
||||
const tbody = el('zoneTableBody');
|
||||
if (!rows.length) { tbody.innerHTML = '<tr><td colspan="5" style="color:var(--muted);text-align:center;padding:1.5rem">No zone data yet</td></tr>'; return; }
|
||||
if (!rows.length) { tbody.innerHTML = '<tr><td colspan="4" style="color:var(--muted);text-align:center;padding:1.5rem">No zone data yet</td></tr>'; return; }
|
||||
tbody.innerHTML = rows.map(r => `
|
||||
<tr>
|
||||
<td style="color:var(--accent)">${r.zone_id}</td>
|
||||
<td>${r.call_count}</td>
|
||||
<td><span class="${r.avg_quality > 75 ? 'badge-green' : r.avg_quality > 50 ? 'badge-amber' : 'badge-red'} badge">${r.avg_quality}%</span></td>
|
||||
<td><span class="${r.sla_breaches === 0 ? 'badge-green' : r.sla_breaches < 3 ? 'badge-amber' : 'badge-red'} badge">${r.sla_breaches}</span></td>
|
||||
<td>${r.avg_distance_km.toFixed(1)} km</td>
|
||||
</tr>`).join('');
|
||||
}
|
||||
@@ -1410,35 +1307,6 @@
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// ─────────────────────────── STRATEGY ───────────────────────────
|
||||
function renderStrategyGrid(current) {
|
||||
activeStrategy = current;
|
||||
const badge = el('activeStrategyBadge');
|
||||
badge.textContent = STRATEGIES[current]?.name || current;
|
||||
|
||||
const grid = el('strategyGrid');
|
||||
grid.innerHTML = Object.entries(STRATEGIES).map(([key, s]) => `
|
||||
<div class="strategy-card ${key === current ? 'active' : ''}" onclick="setStrategy('${key}')">
|
||||
<div class="strategy-name" style="color:${s.color}">${s.name}</div>
|
||||
<div class="strategy-desc">${s.desc}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
async function setStrategy(key) {
|
||||
try {
|
||||
await fetch(`${API}/config`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ml_strategy: key })
|
||||
});
|
||||
toast(`Strategy set: ${STRATEGIES[key].name}`);
|
||||
await fullRefresh();
|
||||
} catch (e) {
|
||||
toast('Failed to set strategy', true);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── ACTIONS ───────────────────────────
|
||||
async function triggerTrain() {
|
||||
const btn = el('btnTrain');
|
||||
|
||||
Reference in New Issue
Block a user