fix(webhooks): 使用 ApprovalRequestCreate + create_approval_with_fingerprint

與 /alerts 端點同步:
- 使用 ApprovalRequestCreate 物件
- 呼叫 create_approval_with_fingerprint()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-24 22:23:17 +08:00
parent 10adce21ad
commit 6f250d8b5f

View File

@@ -1194,48 +1194,71 @@ async def alertmanager_webhook(
if analysis_result:
# analysis_result 是 OpenClawDecision Pydantic 模型
risk_level = analysis_result.risk_level.value # enum → "low", "medium", "critical"
action = f"{analysis_result.action_title} | {analysis_result.kubectl_command}"
root_cause = analysis_result.description or message
estimated_downtime = analysis_result.blast_radius.estimated_downtime if analysis_result.blast_radius else "~30s"
primary_responsibility = analysis_result.primary_responsibility or "COLLAB" # 已是 str
confidence = analysis_result.confidence
# 轉換風險等級
risk_mapping = {
"low": RiskLevel.LOW,
"medium": RiskLevel.MEDIUM,
"critical": RiskLevel.CRITICAL,
}
risk_level = risk_mapping.get(analysis_result.risk_level.value, RiskLevel.MEDIUM)
# 建立 ApprovalRecord
approval = await service.create_approval(
alert_type=alert_type,
target=target_resource,
action=action,
# 提取爆炸半徑
blast = analysis_result.blast_radius
impact_mapping = {
"NONE": DataImpact.NONE,
"READ_ONLY": DataImpact.READ_ONLY,
"WRITE": DataImpact.WRITE,
"DESTRUCTIVE": DataImpact.DESTRUCTIVE,
}
data_impact = impact_mapping.get(blast.data_impact.value, DataImpact.NONE) if blast else DataImpact.NONE
# 建立 ApprovalRequestCreate (同 /alerts 流程)
approval_create = ApprovalRequestCreate(
action=f"{analysis_result.action_title} | {analysis_result.kubectl_command}",
description=f"[AI: {ai_provider}] {analysis_result.description}",
risk_level=risk_level,
details={
"source": "alertmanager",
"alertname": alertname,
"namespace": namespace,
"message": message,
"ai_analysis": analysis_result.model_dump(), # 轉換為字典
"ai_provider": ai_provider,
},
blast_radius=BlastRadius(
affected_pods=blast.affected_pods if blast else 1,
estimated_downtime=blast.estimated_downtime if blast else "~30s",
related_services=list(set((blast.related_services if blast else []) + analysis_result.affected_services)),
data_impact=data_impact,
),
dry_run_checks=[
DryRunCheck(name="AI 信心度", passed=analysis_result.confidence >= 0.7, message=f"{analysis_result.confidence:.0%}"),
DryRunCheck(name="來源", passed=True, message="alertmanager"),
],
requested_by=f"OpenClaw ({ai_provider})",
)
# 使用 create_approval_with_fingerprint (同 /alerts)
approval = await service.create_approval_with_fingerprint(
request=approval_create,
fingerprint=fingerprint,
)
root_cause = analysis_result.description or message
estimated_downtime = blast.estimated_downtime if blast else "~30s"
primary_responsibility = analysis_result.primary_responsibility or "COLLAB"
confidence = analysis_result.confidence
# 推送 Telegram
background_tasks.add_task(
_push_to_telegram_background,
approval_id=str(approval.id),
risk_level=risk_level,
risk_level=risk_level.value,
resource_name=target_resource,
root_cause=root_cause,
suggested_action=analysis_result.suggested_action.value, # 使用 enum value
suggested_action=analysis_result.suggested_action.value,
estimated_downtime=estimated_downtime,
hit_count=1,
primary_responsibility=primary_responsibility,
confidence=confidence,
namespace=namespace,
signoz_rps=signoz_metrics.get("rps", 0) if signoz_metrics else 0,
signoz_rps_trend=signoz_metrics.get("rps_trend", "stable") if signoz_metrics else "stable",
signoz_error_rate=signoz_metrics.get("error_rate", 0) if signoz_metrics else 0,
signoz_p99_latency=signoz_metrics.get("p99_latency", 0) if signoz_metrics else 0,
signoz_latency_trend=signoz_metrics.get("latency_trend", "stable") if signoz_metrics else "stable",
signoz_rps=signoz_metrics.rps if signoz_metrics else 0,
signoz_rps_trend=signoz_metrics.rps_trend if signoz_metrics else "stable",
signoz_error_rate=signoz_metrics.error_rate if signoz_metrics else 0,
signoz_p99_latency=signoz_metrics.p99_latency_ms if signoz_metrics else 0,
signoz_latency_trend=signoz_metrics.latency_trend if signoz_metrics else "stable",
signoz_trace_url=signoz_trace_url or "",
)
@@ -1245,26 +1268,29 @@ async def alertmanager_webhook(
alert_id=alert_id,
approval_created=True,
approval_id=str(approval.id),
risk_level=risk_level,
suggested_action=action,
risk_level=risk_level.value,
suggested_action=approval_create.action,
hit_count=1,
converged=False,
)
else:
# LLM 失敗 - 使用預設值
approval = await service.create_approval(
alert_type=alert_type,
target=target_resource,
fallback_create = ApprovalRequestCreate(
action="OBSERVE",
risk_level="medium",
details={
"source": "alertmanager",
"alertname": alertname,
"namespace": namespace,
"message": message,
"ai_analysis": None,
"ai_error": "LLM analysis failed",
},
description=f"[LLM Failed] {message}",
risk_level=RiskLevel.MEDIUM,
blast_radius=BlastRadius(
affected_pods=1,
estimated_downtime="unknown",
related_services=[],
data_impact=DataImpact.NONE,
),
dry_run_checks=[],
requested_by="OpenClaw (fallback)",
)
approval = await service.create_approval_with_fingerprint(
request=fallback_create,
fingerprint=fingerprint,
)