Files
tracer-cloud--opensre/core/domain/alerts/extraction.py
T
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

155 lines
4.9 KiB
Python

"""Deterministic alert normalization helpers for extract_alert."""
from __future__ import annotations
import json
from collections.abc import Mapping
from typing import Any
from pydantic import BaseModel, Field
from core.domain.alerts.fields import (
alert_annotations,
alert_labels,
alert_name_value,
canonical_alert,
pipeline_name_value,
severity_value,
)
CANONICAL_ALERT_SOURCES = frozenset({"opensre", "opensre_dataset"})
RAW_ALERT_DETAIL_FIELDS = (
"kube_namespace",
"cloudwatch_log_group",
"error_message",
"log_query",
"eks_cluster",
"pod_name",
"deployment",
)
class AlertDetails(BaseModel):
"""Normalized alert fields produced by the extract_alert stage."""
is_noise: bool = Field(default=False)
alert_name: str = Field(default="unknown")
pipeline_name: str = Field(default="unknown")
severity: str = Field(default="unknown")
alert_source: str | None = Field(default=None)
environment: str | None = Field(default=None)
summary: str | None = Field(default=None)
kube_namespace: str | None = Field(default=None)
cloudwatch_log_group: str | None = Field(default=None)
error_message: str | None = Field(default=None)
log_query: str | None = Field(default=None)
eks_cluster: str | None = Field(default=None)
pod_name: str | None = Field(default=None)
deployment: str | None = Field(default=None)
def format_raw_alert(raw_alert: Any) -> str:
"""Render raw alert payload as prompt text for the extract_alert LLM call."""
if isinstance(raw_alert, str):
return raw_alert
if isinstance(raw_alert, dict):
if raw_alert.get("text") and not needs_full_json_prompt(raw_alert):
return str(raw_alert["text"])
return json.dumps(raw_alert, indent=2, sort_keys=True)
return json.dumps(raw_alert, indent=2, sort_keys=True)
def needs_full_json_prompt(raw_alert: dict[str, Any]) -> bool:
"""Return True when extract_alert should receive full JSON instead of text-only."""
src = str(raw_alert.get("alert_source", "")).lower()
if src in CANONICAL_ALERT_SOURCES:
return True
if (
raw_alert.get("commonLabels")
or raw_alert.get("commonAnnotations")
or raw_alert.get("alerts")
):
return True
for key in (
"opensre_telemetry_relative",
"opensre_dataset_root",
):
if raw_alert.get(key):
return True
ann = raw_alert.get("commonAnnotations")
if isinstance(ann, dict) and ann.get(key):
return True
meta = raw_alert.get("_meta")
return bool(isinstance(meta, dict) and "opensre" in str(meta.get("purpose", "")).lower())
def fallback_details(state: Mapping[str, Any], raw_alert: Any) -> AlertDetails:
"""Best-effort field extraction when the LLM path is unavailable."""
alert_name = state.get("alert_name", "unknown")
pipeline_name = state.get("pipeline_name", "unknown")
severity = state.get("severity", "unknown")
if isinstance(raw_alert, dict):
labels = alert_labels(raw_alert)
annotations = alert_annotations(raw_alert)
canonical = canonical_alert(raw_alert)
alert_name = alert_name_value(
raw_alert,
labels=labels,
annotations=annotations,
canonical=canonical,
fallback=alert_name,
)
pipeline_name = pipeline_name_value(
raw_alert,
labels=labels,
annotations=annotations,
canonical=canonical,
fallback=pipeline_name,
)
severity = severity_value(
raw_alert,
labels=labels,
canonical=canonical,
fallback=severity,
)
return AlertDetails(
is_noise=False,
alert_name=alert_name or "unknown",
pipeline_name=pipeline_name or "unknown",
severity=severity or "unknown",
)
def make_problem_md(details: AlertDetails) -> str:
"""Build the operator-facing problem markdown header from extracted details."""
parts = [
f"# {details.alert_name}",
f"Pipeline: {details.pipeline_name} | Severity: {details.severity}",
]
if details.kube_namespace:
parts.append(f"Namespace: {details.kube_namespace}")
if details.error_message:
parts.append(f"\nError: {details.error_message}")
return "\n".join(parts)
def enrich_raw_alert(raw_alert: Any, details: AlertDetails) -> Any:
"""Merge extracted details back into the raw alert dict for downstream stages."""
if not isinstance(raw_alert, dict):
raw_alert = {}
enriched = dict(raw_alert)
prior_source = str(raw_alert.get("alert_source", "")).lower()
for field_name in RAW_ALERT_DETAIL_FIELDS:
value = getattr(details, field_name)
if value:
enriched[field_name] = value
if details.alert_source and prior_source not in CANONICAL_ALERT_SOURCES:
enriched["alert_source"] = details.alert_source
return enriched