feat(p55): 3 個圓餅圖補齊 — promotion_review/ppt_audit/budget
All checks were successful
CD Pipeline / deploy (push) Successful in 7m39s

S-1: promotion_review 蒸餾池 30d doughnut
- 取代原 col-md-2 卡片網格
- 8 種狀態各自分色:
  pending(灰) / awaiting_review(黃) / approved(綠) /
  rejected_quality(紅) / rejected_hallucination(深紅) /
  rejected_duplicate(橘) / rejected_human(暗紅) / expired(灰)
- 左圓餅 + 右表格雙視角

S-2: ppt_audit 30d 結果 doughnut
- 取代部分 col-md-2 卡片佈局
- 通過(綠)/失敗(黃)/錯誤(紅)/跳過(灰) 圓餅
- 6 個 KPI 卡併入右側 col-6 grid(總筆數/通過率/通過/issue/失敗/錯誤)
- 統一視覺語言:「圖+表」雙視角

S-3: budget 當月各 provider 成本 doughnut
- 新加 query:ai_calls.cost_usd GROUP BY provider 月初至今
- 8 個 provider 分色(本地 Ollama 綠系 vs 付費 LLM 橘紫系)
- 左圓餅 + 右表格(供應商/成本/佔比)+ 總計列

chart.js 視覺化從 7 個 → 10 個:
- hourly trend line
- 30d cost stacked bar
- 三主機 sparkline × 3
- RAG feedback doughnut
- KPI sparkline × 3 (calls/cost/errors)
- verdict doughnut
- heal 7d trend
- **promotion_review status doughnut(新)**
- **ppt_audit pass/fail doughnut(新)**
- **provider cost doughnut(新)**

Phase 38→55 累計 20 commits / 10 觀測頁 / 10 chart.js / DB 100%。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
OoO
2026-05-05 01:15:58 +08:00
parent 90e8366a8d
commit df2311d4f0
4 changed files with 241 additions and 55 deletions

View File

@@ -99,6 +99,44 @@
</tbody>
</table>
<!-- Phase 55 S-3: 當月各 provider 成本分布圓餅 -->
{% if provider_cost_month %}
<div class="card mb-3">
<div class="card-header"><strong><i class="fas fa-chart-pie me-2"></i>當月各供應商成本分布</strong>
<small class="text-muted">資料來源ai_calls.cost_usd GROUP BY provider · 月初至今</small>
</div>
<div class="card-body">
<div class="row g-2 align-items-center">
<div class="col-md-5">
<canvas id="providerCostPieChart" height="180"></canvas>
</div>
<div class="col-md-7">
<table class="table table-sm mb-0" style="font-size: 0.9em;">
<thead class="table-light">
<tr><th>供應商</th><th class="text-end">成本</th><th class="text-end">佔比</th></tr>
</thead>
<tbody>
{% set total_pc = (provider_cost_month | sum(attribute='cost')) or 0.0001 %}
{% for p in provider_cost_month %}
<tr>
<td><code>{{ p.provider }}</code></td>
<td class="text-end">${{ "%.4f"|format(p.cost) }}</td>
<td class="text-end">{{ "%.1f"|format(p.cost / total_pc * 100) }}%</td>
</tr>
{% endfor %}
<tr style="border-top: 2px solid #dee2e6;">
<td><strong>總計</strong></td>
<td class="text-end"><strong>${{ "%.2f"|format(total_pc) }}</strong></td>
<td class="text-end"><strong>100%</strong></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endif %}
<!-- Phase 47 K-3: Top 5 cost-burning caller -->
{% if top_cost_callers %}
<div class="card mb-3">
@@ -173,6 +211,39 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script>
// Phase 55 S-3: provider 月度成本圓餅
(function() {
const data = {{ provider_cost_month | default([]) | tojson }};
const el = document.getElementById('providerCostPieChart');
if (!el || !data.length) return;
const colors = {
'gcp_ollama': '#28a745', 'ollama_secondary': '#5cb85c', 'ollama_111': '#a3d9a4',
'gemini': '#fd7e14', 'claude': '#6610f2', 'nim': '#0dcaf0',
'openrouter': '#6c757d', 'nim_via_elephant': '#20c997',
};
new Chart(el, {
type: 'doughnut',
data: {
labels: data.map(d => d.provider),
datasets: [{
data: data.map(d => d.cost),
backgroundColor: data.map((d, i) => colors[d.provider] || `hsl(${(i*47)%360}, 65%, 55%)`),
borderWidth: 1, borderColor: '#fff',
}]
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: {
legend: { position: 'right', labels: { font: { size: 11 } } },
tooltip: { callbacks: { label: c => {
const total = data.reduce((a,b)=>a+b.cost, 0);
return `${c.label}: $${c.parsed.toFixed(4)} (${(c.parsed/total*100).toFixed(1)}%)`;
}}}
}
}
});
})();
// Phase 50 N-2: 30d cost trend stacked bar chart
(function() {
const raw = {{ cost_trend_30d | tojson }};