feat: Sprint 4 Phase E+F — 前端處置統計 + 週報處置分佈
Phase E: 前端頁面 - E1: /reports 完整處置統計儀表板 (已在 Sprint F 完成) - E2: 首頁 Metrics Strip — 從 disposition API 取得真實自動化率 優先使用 /stats/disposition auto_rate,fallback 到 incidents 推算 - E3: /auto-repair 處置概況卡片 (已在 Sprint F 完成) - E4: /neural-command stats tab 處置分佈 (已在 Sprint F 完成) - E5: i18n 翻譯 zh-TW + en (已在 Sprint F 完成) Phase F: 週報 + 文件 - F1: WeeklyReportMessage 新增 disposition 5 欄位 週報格式加「📋 處置分佈」區塊 (自動/冷啟動/人工/手動 + 自動化率) weekly_report_service 整合 get_all_disposition_stats() - message 字數上限從 900 提升到 1200 (適應處置區塊) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -978,6 +978,12 @@ class WeeklyReportMessage:
|
||||
# 成本
|
||||
ai_cost_week: float = 0.0
|
||||
ai_tokens_week: int = 0
|
||||
# 2026-04-07 Claude Code: Sprint 4 F1 — 處置分佈
|
||||
disposition_auto: int = 0
|
||||
disposition_human: int = 0
|
||||
disposition_manual: int = 0
|
||||
disposition_cold_start: int = 0
|
||||
disposition_total: int = 0
|
||||
|
||||
def format(self) -> str:
|
||||
"""格式化為 Telegram HTML"""
|
||||
@@ -1015,10 +1021,24 @@ class WeeklyReportMessage:
|
||||
f"━━━━━━━━━━━━━━━━━━━\n"
|
||||
f"💰 <b>AI 成本</b>\n"
|
||||
f"├ 費用: $<code>{self.ai_cost_week:.2f}</code>\n"
|
||||
f"└ Tokens: <code>{self.ai_tokens_week:,}</code>"
|
||||
f"└ Tokens: <code>{self.ai_tokens_week:,}</code>\n"
|
||||
)
|
||||
|
||||
return message[:900]
|
||||
# Sprint 4 F1: 處置分佈(有資料才加)
|
||||
if self.disposition_total > 0:
|
||||
auto_total = self.disposition_auto + self.disposition_cold_start
|
||||
auto_rate = int(auto_total / self.disposition_total * 100) if self.disposition_total > 0 else 0
|
||||
message += (
|
||||
f"━━━━━━━━━━━━━━━━━━━\n"
|
||||
f"📋 <b>處置分佈</b>\n"
|
||||
f"├ 🤖 自動修復: <code>{self.disposition_auto}</code>\n"
|
||||
f"├ ❄️ 冷啟動信任: <code>{self.disposition_cold_start}</code>\n"
|
||||
f"├ 👤 人工審核: <code>{self.disposition_human}</code>\n"
|
||||
f"├ 🔧 手動處理: <code>{self.disposition_manual}</code>\n"
|
||||
f"└ 自動化率: <b>{auto_rate}%</b>"
|
||||
)
|
||||
|
||||
return message[:1200]
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -178,6 +178,20 @@ class WeeklyReportService:
|
||||
pod_restarts = k3s_status.pod_restart_48h if k3s_status else 0
|
||||
hpa_events = 0 # 需要從 Prometheus 取得 HPA 事件
|
||||
|
||||
# 2026-04-07 Claude Code: Sprint 4 F1 — 取得處置分佈
|
||||
disp_auto = disp_human = disp_manual = disp_cold = disp_total = 0
|
||||
try:
|
||||
from src.services.anomaly_counter import get_anomaly_counter
|
||||
counter = get_anomaly_counter()
|
||||
disp_summary, _ = await counter.get_all_disposition_stats()
|
||||
disp_auto = disp_summary.get("auto_repair", 0)
|
||||
disp_human = disp_summary.get("human_approved", 0)
|
||||
disp_manual = disp_summary.get("manual_resolved", 0)
|
||||
disp_cold = disp_summary.get("cold_start_trust", 0)
|
||||
disp_total = disp_summary.get("total", 0)
|
||||
except Exception as _disp_e:
|
||||
logger.warning("weekly_report_disposition_failed", error=str(_disp_e))
|
||||
|
||||
# 組裝週報
|
||||
report = WeeklyReportMessage(
|
||||
week_range=week_range,
|
||||
@@ -197,6 +211,11 @@ class WeeklyReportService:
|
||||
deploy_count=deploys,
|
||||
ai_cost_week=0.0, # 需要從 AI 成本追蹤取得
|
||||
ai_tokens_week=0, # 需要從 AI 成本追蹤取得
|
||||
disposition_auto=disp_auto,
|
||||
disposition_human=disp_human,
|
||||
disposition_manual=disp_manual,
|
||||
disposition_cold_start=disp_cold,
|
||||
disposition_total=disp_total,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
|
||||
@@ -365,8 +365,22 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
// P0 count
|
||||
const p0Count = incidents?.filter(i => i.severity === 'P0').length ?? 0
|
||||
|
||||
// 自動處置率
|
||||
// 2026-04-07 Claude Code: Sprint 4 E2 — 從 disposition API 取得真實自動化率
|
||||
const [dispositionRate, setDispositionRate] = useState<{ auto_rate: number; total: number } | null>(null)
|
||||
useEffect(() => {
|
||||
fetch(`${API_BASE}/api/v1/stats/disposition`)
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
if (d?.summary) setDispositionRate({ auto_rate: d.summary.auto_rate, total: d.summary.total })
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [])
|
||||
|
||||
// 自動處置率 — 優先使用 disposition API,fallback 到 incidents 推算
|
||||
const autoRemediationRate = (() => {
|
||||
if (dispositionRate && dispositionRate.total > 0) {
|
||||
return `${Math.round(dispositionRate.auto_rate * 100)}%`
|
||||
}
|
||||
if (!incidents?.length) return '--'
|
||||
const resolved = incidents.filter(i => i.status === 'resolved' || i.status === 'closed').length
|
||||
return `${((resolved / incidents.length) * 100).toFixed(0)}%`
|
||||
@@ -374,6 +388,9 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
|
||||
// 自動處置率數值 (for progress bar)
|
||||
const autoRemediationPct = (() => {
|
||||
if (dispositionRate && dispositionRate.total > 0) {
|
||||
return Math.round(dispositionRate.auto_rate * 100)
|
||||
}
|
||||
if (!incidents?.length) return 0
|
||||
const resolved = incidents.filter(i => i.status === 'resolved' || i.status === 'closed').length
|
||||
return Math.round((resolved / incidents.length) * 100)
|
||||
|
||||
Reference in New Issue
Block a user