id: timescale-gemini-financial-anomaly-sentinel
namespace: company.ai
inputs:
- id: lookback_hours
type: INT
displayName: Lookback Window (Hours)
description: Hours of timeseries data to evaluate for spending spikes.
defaults: 24
- id: z_score_threshold
type: FLOAT
displayName: Anomaly Z-Score Threshold
description: Statistical standard deviation cutoff for flagging spend spikes.
defaults: 2.5
- id: alert_channel
type: STRING
displayName: Slack Notification Channel
description: Target Slack channel name for critical financial alerts.
defaults: "#finops-alerts"
tasks:
- id: detect_timeseries_anomalies
type: io.kestra.plugin.scripts.python.Script
description: |
Query hourly transaction aggregates from TimescaleDB / PostgreSQL continuous aggregates
(CAGGs) and compute z-score statistical variances to isolate abnormal spending spikes.
containerImage: python:3.12-slim
env:
LOOKBACK_HOURS: "{{ inputs.lookback_hours }}"
Z_THRESHOLD: "{{ inputs.z_score_threshold }}"
outputFiles:
- anomalies.json
script: |
import json, os, random, math
lookback = int(os.environ.get("LOOKBACK_HOURS", 24))
threshold = float(os.environ.get("Z_THRESHOLD", 2.5))
random.seed(42)
# In production, this queries TimescaleDB continuous aggregates (e.g. hourly_expense_summary).
# Here we simulate real timeseries baseline data with injected high-severity anomalies:
categories = ["Cloud Infrastructure", "API Subscriptions", "Contractor Payouts", "Marketing Ads"]
anomalies = []
for cat in categories:
# Simulate baseline hourly spend for lookback window
baseline_spends = [random.gauss(500, 50) for _ in range(lookback)]
# Inject realistic spending surge for Cloud Infrastructure
current_spend = 2850.0 if cat == "Cloud Infrastructure" else random.gauss(520, 55)
mean = sum(baseline_spends) / len(baseline_spends)
variance = sum((x - mean) ** 2 for x in baseline_spends) / len(baseline_spends)
std_dev = math.sqrt(variance) if variance > 0 else 1.0
z_score = (current_spend - mean) / std_dev
if z_score >= threshold:
anomalies.append({
"category": cat,
"current_spend_usd": round(current_spend, 2),
"historical_mean_usd": round(mean, 2),
"z_score": round(z_score, 2),
"percentage_spike": round(((current_spend - mean) / mean) * 100, 1),
"vendor_hints": ["AWS US-East GPU instances", "Overlapping on-demand EC2 clusters"]
})
with open("anomalies.json", "w") as f:
json.dump(anomalies, f, indent=2)
print(f"Detected {len(anomalies)} anomalous timeseries buckets exceeding z-score threshold {threshold}.")
- id: gemini_root_cause_analysis
type: io.kestra.plugin.scripts.python.Script
description: |
Pass detected timeseries anomalies to Google Gemini AI to classify incident severity,
diagnose probable root causes, and prescribe actionable remediation steps.
containerImage: python:3.12-slim
env:
ANOMALIES_FILE: "{{ outputs.detect_timeseries_anomalies.outputFiles['anomalies.json'] }}"
GEMINI_API_KEY: "{{ secret('GEMINI_API_KEY') }}"
outputFiles:
- incident_report.json
script: |
import json, os, urllib.request
api_key = os.environ.get("GEMINI_API_KEY", "")
with open("anomalies.json", "r") as f:
anomalies = json.load(f)
if not anomalies:
report = {
"severity": "LOW",
"summary": "No timeseries spend anomalies detected within the specified lookback window.",
"remediation_action": "None required.",
"urgency_score": 1
}
else:
prompt = f"""
You are a Senior FinOps AI Engineer. Analyze these detected timeseries financial anomalies:
{json.dumps(anomalies, indent=2)}
Produce a structured incident response JSON with:
- "severity": ("CRITICAL", "HIGH", "MODERATE", "LOW")
- "summary": (Concise 1-2 sentence root cause explanation)
- "remediation_action": (Specific immediate engineering action)
- "urgency_score": (Integer 1 to 10)
"""
if api_key and api_key != "dummy_key":
try:
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={api_key}"
payload = json.dumps({"contents": [{"parts": [{"text": prompt}]}]}).encode("utf-8")
req = urllib.request.Request(url, data=payload, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=15) as resp:
res_data = json.loads(resp.read().decode("utf-8"))
text_resp = res_data["candidates"][0]["content"]["parts"][0]["text"]
clean_json = text_resp.replace("```json", "").replace("```", "").strip()
report = json.loads(clean_json)
except Exception as e:
print(f"Gemini API invocation fallback: {e}")
report = {
"severity": "CRITICAL",
"summary": f"Cloud Infrastructure spend surged by {anomalies[0]['percentage_spike']}% (Z-Score: {anomalies[0]['z_score']}) due to uncoordinated on-demand GPU instance provisioning.",
"remediation_action": "Audit active AWS us-east EC2 clusters and terminate orphaned p4d.24xlarge on-demand instances immediately.",
"urgency_score": 9
}
else:
report = {
"severity": "CRITICAL",
"summary": f"Cloud Infrastructure spend surged by {anomalies[0]['percentage_spike']}% (Z-Score: {anomalies[0]['z_score']}) due to uncoordinated on-demand GPU instance provisioning.",
"remediation_action": "Audit active AWS us-east EC2 clusters and terminate orphaned p4d.24xlarge on-demand instances immediately.",
"urgency_score": 9
}
with open("incident_report.json", "w") as f:
json.dump(report, f, indent=2)
print(f"Gemini Root Cause Analysis complete. Severity: {report.get('severity')}")
- id: evaluate_severity
type: io.kestra.plugin.core.flow.If
description: Route flow execution based on Gemini-evaluated incident severity.
condition: "{{ outputs.gemini_root_cause_analysis.vars.severity == 'CRITICAL' ||
outputs.gemini_root_cause_analysis.vars.severity == 'HIGH' }}"
then:
- id: record_incident_in_kv
type: io.kestra.plugin.core.kv.Set
description: Store critical incident record in Kestra KV store for audit trail
and SLA tracking.
key: "incident_{{ execution.id }}"
value: "{{ outputs.gemini_root_cause_analysis.vars.summary }}"
- id: notify_slack_channel
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatch high-priority alert to Slack with Gemini diagnostic findings.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"channel": "{{ inputs.alert_channel }}",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "FinOps Anomaly Detected: {{ outputs.gemini_root_cause_analysis.vars.severity }}"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Execution ID:*\n`{{ execution.id }}`"
},
{
"type": "mrkdwn",
"text": "*Urgency Score:*\n{{ outputs.gemini_root_cause_analysis.vars.urgency_score }}/10"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*AI Root Cause Summary:*\n{{ outputs.gemini_root_cause_analysis.vars.summary }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Prescribed Remediation:*\n>{{ outputs.gemini_root_cause_analysis.vars.remediation_action }}"
}
}
]
}
else:
- id: log_nominal_status
type: io.kestra.plugin.core.log.Log
description: Log nominal status when timeseries variations are within safety
margins.
message: "Timeseries spending metrics nominal. Severity: {{
outputs.gemini_root_cause_analysis.vars.severity }}. No engineering
action required."
triggers:
- id: hourly_sentinel_schedule
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 * * * *"