Files
ewoooc/templates/admin/ai_calls_dashboard.html
OoO 19f1340f5c
All checks were successful
CD Pipeline / deploy (push) Successful in 2m42s
feat(p38): admin 觀測台 6 頁完整繁中化 + 加入導航選單
問題:
1. 6 個 /observability/* 頁面標題與欄位英文殘留(違反設計憲法繁中要求)
2. 6 頁完全沒掛 navbar,使用者進不去(只能彼此 footer link 互連)
3. emoji 取代 Font Awesome,違反設計規範

修補:
- _navbar.html 新增「AI 觀測台」dropdown(位於 AI 助手 與 雲端匯入 之間)
  - AI 監控組:AI 呼叫總覽 / 主機健康監控 / 預算控管
  - AI 學習組:RAG 學習晉升審核 / Caller 反饋趨勢 / PPT 視覺審核歷史
- 6 個 admin/observability template 全面繁中化:
  - 標題、表格欄位、按鈕、badge 文字、JS alert 文案
  - emoji → Font Awesome icon(fa-heartbeat / fa-chart-bar / fa-wallet / fa-brain / fa-comments / fa-search 等)
  - 移除 5 處 footer 手寫 link 條(已由 navbar 取代,避免雙寫)
- routes/admin_observability_routes.py 6 個 render_template 加 active_page='obs_*'
  讓 navbar dropdown 正確高亮

完整覆蓋:host_health / ai_calls_dashboard / budget / promotion_review / quality_trend / ppt_audit_history

設計規範對齊:仍待 Phase 後續工作(ewoooc_base.html 框架升級 + --momo-* design token)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 18:49:44 +08:00

120 lines
5.3 KiB
HTML

{% extends "base.html" %}
{% block title %}AI 呼叫總覽{% endblock %}
{% block content %}
<div class="container-fluid mt-3">
<h2 class="mb-3"><i class="fas fa-chart-bar me-2"></i>AI 呼叫總覽
<small class="text-muted">過去 {{ hours }} 小時</small>
</h2>
{% if error %}
<div class="alert alert-warning"><strong><i class="fas fa-exclamation-triangle me-1"></i></strong> {{ error }}</div>
{% endif %}
<!-- 篩選 bar -->
<form method="get" class="row g-2 mb-3">
<div class="col-auto">
<select name="hours" class="form-select form-select-sm">
{% for h in [1, 6, 24, 72, 168] %}
<option value="{{ h }}" {% if hours == h %}selected{% endif %}>
{% if h < 24 %}{{ h }} 小時{% else %}{{ (h//24) }} {% endif %}
</option>
{% endfor %}
</select>
</div>
<div class="col-auto">
<select name="caller" class="form-select form-select-sm">
<option value="">全部呼叫端</option>
{% for c in callers %}
<option value="{{ c }}" {% if caller_filter == c %}selected{% endif %}>{{ c }}</option>
{% endfor %}
</select>
</div>
<div class="col-auto">
<select name="provider" class="form-select form-select-sm">
<option value="">全部供應商</option>
{% for p in ['gcp_ollama','ollama_secondary','ollama_111','gemini','claude','nim','openrouter','nim_via_elephant'] %}
<option value="{{ p }}" {% if provider_filter == p %}selected{% endif %}>{{ p }}</option>
{% endfor %}
</select>
</div>
<div class="col-auto">
<button class="btn btn-primary btn-sm">篩選</button>
</div>
</form>
<!-- 總覽 KPI -->
<div class="row g-2 mb-3">
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>呼叫次數</small><h4>{{ "{:,}".format(summary.total_calls or 0) }}</h4></div></div>
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>Token 用量</small><h4>{{ "{:,}".format(summary.total_tokens or 0) }}</h4></div></div>
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>成本 (USD)</small><h4>${{ "%.2f"|format(summary.total_cost or 0) }}</h4></div></div>
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>平均耗時</small><h4>{{ summary.avg_duration or 0 }} ms</h4></div></div>
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>RAG 命中</small><h4 class="text-success">{{ summary.rag_hits or 0 }}</h4></div></div>
<div class="col-lg-2 col-md-4 col-sm-6"><div class="card p-2"><small>錯誤次數</small><h4 class="{% if (summary.error_calls or 0) > 0 %}text-danger{% endif %}">{{ summary.error_calls or 0 }}</h4></div></div>
</div>
<!-- by provider -->
<div class="card mb-3">
<div class="card-header"><strong>依供應商分組</strong></div>
<div class="card-body p-0">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr><th>供應商</th><th class="text-end">呼叫數</th><th class="text-end">Token 用量</th><th class="text-end">成本 (USD)</th></tr>
</thead>
<tbody>
{% for row in by_provider %}
<tr>
<td><code>{{ row.provider }}</code></td>
<td class="text-end">{{ "{:,}".format(row.calls) }}</td>
<td class="text-end">{{ "{:,}".format(row.tokens) }}</td>
<td class="text-end">${{ "%.2f"|format(row.cost) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- recent calls -->
<div class="card">
<div class="card-header"><strong>最近呼叫(最新 100 筆)</strong></div>
<div class="card-body p-0">
<table class="table table-sm table-striped mb-0" style="font-size: 0.85em;">
<thead class="table-light">
<tr>
<th>編號</th><th>時間</th><th>呼叫端</th><th>供應商</th><th>模型</th>
<th class="text-end">輸入</th><th class="text-end">輸出</th><th class="text-end">耗時 ms</th>
<th>狀態</th><th class="text-end">成本 $</th><th>標記</th>
</tr>
</thead>
<tbody>
{% for r in recent %}
<tr {% if r.status not in ['ok','cache_only'] %}class="table-warning"{% endif %}>
<td>{{ r.id }}</td>
<td><small>{{ r.called_at }}</small></td>
<td><code>{{ r.caller }}</code></td>
<td><small>{{ r.provider }}</small></td>
<td><small>{{ r.model[:25] }}</small></td>
<td class="text-end">{{ r.in_tokens }}</td>
<td class="text-end">{{ r.out_tokens }}</td>
<td class="text-end">{{ r.duration_ms }}</td>
<td><small>{{ r.status }}</small></td>
<td class="text-end">${{ "%.4f"|format(r.cost) }}</td>
<td>
{% if r.cache_hit %}<span class="badge bg-success">快取</span>{% endif %}
{% if r.rag_hit %}<span class="badge bg-info">RAG</span>{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<p class="text-muted mt-2"><small>
<i class="fas fa-robot me-1"></i>Operation Ollama-First v5.0 / Phase 29 — AI 呼叫總覽
</small></p>
</div>
{% endblock %}