Initial commit

This commit is contained in:
denshooter
2026-03-09 22:07:19 +01:00
commit daef092099
55 changed files with 39435 additions and 0 deletions
+875
View File
@@ -0,0 +1,875 @@
import asyncio
import json
import re
import shutil
from datetime import datetime, timezone
import httpx
OLLAMA_URL = "http://localhost:11434/api/generate"
MODEL_NAME = "llama3.2"
# CLI backend: "claude" uses Claude Code CLI (no API key needed if logged in),
# "gemini" uses Gemini CLI (free with Google account), "ollama" = local llama3.2
# Auto-detect: prefers claude > gemini > ollama
def _detect_cli_backend() -> str:
for tool in ("gemini", "claude"):
if shutil.which(tool):
return tool
return "ollama"
_CLI_BACKEND = _detect_cli_backend()
async def _query_cli(prompt: str) -> dict | None:
"""Call claude or gemini CLI as subprocess, parse JSON from response."""
try:
cmd = [_CLI_BACKEND, "-p", prompt]
proc = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=30.0)
raw = stdout.decode().strip()
if not raw:
return None
# Strip markdown code fences if present
if "```json" in raw:
raw = raw.split("```json", 1)[1].split("```", 1)[0].strip()
elif "```" in raw:
raw = raw.split("```", 1)[1].split("```", 1)[0].strip()
# Extract first JSON object if there's surrounding text
m = re.search(r'\{.*\}', raw, re.DOTALL)
if m:
raw = m.group(0)
parsed = json.loads(raw)
return parsed if isinstance(parsed, dict) else None
except Exception as exc:
print(f"[AI] CLI query failed ({_CLI_BACKEND}): {exc}")
return None
THREAT_LEVELS = ("LOW", "GUARDED", "ELEVATED", "HIGH", "SEVERE")
KEYWORD_SCORES = {
"war": 4,
"missile": 4,
"strike": 4,
"attack": 4,
"explosion": 3,
"drone": 3,
"nuclear": 5,
"cyber": 3,
"hijack": 4,
"sanction": 2,
"military": 3,
"troops": 3,
"border": 2,
"earthquake": 2,
"tsunami": 4,
}
# Hotspot regions with bounding boxes for spatial movement analysis
# bbox = (lat_min, lon_min, lat_max, lon_max)
HOTSPOT_REGIONS = {
"ukraine": {
"name": "Ukraine / Black Sea", "lat": 48.38, "lon": 31.17,
"bbox": (44, 25, 52, 40),
"keywords": ["ukraine", "kyiv", "kharkiv", "crimea", "donbas", "odesa", "bakhmut", "zaporizhzhia"],
"baseline": "Escalation risk around frontline",
},
"russia": {
"name": "Western Russia", "lat": 55.76, "lon": 37.62,
"bbox": (52, 30, 62, 48),
"keywords": ["russia", "moscow", "kursk"],
"baseline": "Military posture shifts",
},
"israel": {
"name": "Israel / Palestine", "lat": 31.05, "lon": 34.85,
"bbox": (29, 33, 33.5, 36),
"keywords": ["israel", "gaza", "west bank", "tel aviv", "jerusalem", "rafah", "idf", "hamas", "hezbollah"],
"baseline": "Cross-border retaliation risk",
},
"iran": {
"name": "Iran", "lat": 32.43, "lon": 53.69,
"bbox": (25, 44, 40, 63),
"keywords": ["iran", "tehran", "irgc"],
"baseline": "Strategic force posture shift",
},
"syria_lebanon": {
"name": "Syria / Lebanon", "lat": 34.80, "lon": 37.00,
"bbox": (32, 35, 37.5, 42),
"keywords": ["syria", "damascus", "lebanon", "aleppo"],
"baseline": "Proxy activity risk",
},
"taiwan": {
"name": "Taiwan Strait", "lat": 23.70, "lon": 120.96,
"bbox": (21, 117, 27, 124),
"keywords": ["taiwan", "taipei", "taiwan strait"],
"baseline": "Maritime and airspace pressure",
},
"south_china_sea": {
"name": "South China Sea", "lat": 12.0, "lon": 113.0,
"bbox": (5, 107, 22, 120),
"keywords": ["south china sea", "spratly", "paracel"],
"baseline": "Naval maneuvering risk",
},
"korea": {
"name": "Korean Peninsula", "lat": 37.57, "lon": 126.98,
"bbox": (33, 124, 43, 131),
"keywords": ["korea", "pyongyang", "dprk", "north korea", "south korea", "seoul"],
"baseline": "Missile test / military response risk",
},
"red_sea": {
"name": "Red Sea / Bab-el-Mandeb", "lat": 15.0, "lon": 42.0,
"bbox": (10, 38, 22, 46),
"keywords": ["red sea", "houthi", "yemen", "bab-el-mandeb", "aden"],
"baseline": "Shipping disruption risk",
},
"hormuz": {
"name": "Strait of Hormuz", "lat": 26.58, "lon": 56.25,
"bbox": (24, 54, 28, 58),
"keywords": ["hormuz", "persian gulf"],
"baseline": "Energy transit disruption risk",
},
"suez": {
"name": "Suez Canal", "lat": 29.93, "lon": 32.55,
"bbox": (28, 31, 32, 34),
"keywords": ["suez"],
"baseline": "Maritime chokepoint risk",
},
"sahel": {
"name": "Sahel / West Africa", "lat": 14.0, "lon": 0.0,
"bbox": (8, -10, 20, 15),
"keywords": ["mali", "niger", "burkina faso", "sahel", "nigeria"],
"baseline": "Insurgency / instability risk",
},
}
# ── Region definitions for grouping events into theaters ──
REGION_DEFS: dict[str, list[str]] = {
"Eastern Europe": [
"Ukraine", "Russia", "Crimea", "Donbas", "Donbass", "Kharkiv", "Kyiv",
"Kiev", "Odesa", "Mariupol", "Bakhmut", "Zaporizhzhia", "Kherson",
"Moldova", "Belarus", "Avdiivka",
],
"Middle East": [
"Israel", "Gaza", "Palestine", "Lebanon", "Hezbollah", "Syria",
"Damascus", "Iran", "Tehran", "Iraq", "Baghdad", "Jordan",
"West Bank", "Rafah", "Jenin", "Nablus", "Jerusalem", "Tel Aviv",
],
"Red Sea & Horn of Africa": [
"Red Sea", "Yemen", "Houthi", "Somalia", "Sudan", "Ethiopia",
"Bab-el-Mandeb", "Suez",
],
"East Asia": [
"China", "Taiwan", "Taipei", "North Korea", "Pyongyang",
"South Korea", "Japan", "South China Sea", "Taiwan Strait",
"East China Sea",
],
"South Asia": [
"India", "Pakistan", "Afghanistan", "Kashmir", "Myanmar", "Kabul",
],
"Sub-Saharan Africa": [
"Mali", "Niger", "Nigeria", "Congo", "Sahel", "Burkina Faso",
"Mozambique", "Libya",
],
"Europe / NATO": [
"NATO", "Poland", "Finland", "Sweden", "Norway", "Baltic",
"Germany", "France", "UK", "Serbia", "Kosovo", "Georgia", "Romania",
],
}
_WEIGHT_ORDER = {"CRITICAL": 0, "HIGH": 1, "ELEVATED": 2, "GUARDED": 3, "ACTIVE": 4}
def _match_region(title: str) -> str | None:
"""Match a headline to a geopolitical region."""
tl = title.lower()
for region, keywords in REGION_DEFS.items():
for kw in keywords:
if kw.lower() in tl:
return region
return None
def _regional_briefs(news_items: list, conflicts: list) -> list[dict]:
"""Group events by region and produce per-region threat assessments."""
region_events: dict[str, list] = {}
for item in (conflicts or []):
region = _match_region(item.get("title", ""))
if region:
region_events.setdefault(region, []).append(item)
for item in (news_items or [])[:30]:
region = _match_region(item.get("title", ""))
if region:
region_events.setdefault(region, []).append(
{"title": item["title"], "severity": "INFO", "event_type": "INTEL"}
)
briefs = []
for region, events in region_events.items():
sev_counts: dict[str, int] = {}
for ev in events:
s = ev.get("severity", "INFO")
sev_counts[s] = sev_counts.get(s, 0) + 1
critical = sev_counts.get("CRITICAL", 0)
high = sev_counts.get("HIGH", 0)
if critical >= 3:
status = "CRITICAL"
elif critical >= 1 or high >= 3:
status = "HIGH"
elif high >= 1:
status = "ELEVATED"
else:
status = "ACTIVE"
top = [e for e in events if e.get("severity") in ("CRITICAL", "HIGH")][:3]
if top:
summary = "; ".join(
e.get("event_type", e.get("title", "")[:40]) for e in top[:2]
)
else:
summary = f"{len(events)} events tracked in region"
briefs.append({
"region": region,
"status": status,
"event_count": len(events),
"critical": critical,
"high": high,
"summary": summary[:120],
})
briefs.sort(key=lambda b: (_WEIGHT_ORDER.get(b["status"], 5), -b["event_count"]))
return briefs[:6]
def _key_drivers(
score: int, reasons: list[str], gps_data: list, bgp_data: dict,
conflicts: list, space_weather: dict, planes: list = None, ships: list = None,
) -> list[dict]:
"""Identify and rank the top threat signal drivers."""
drivers: list[dict] = []
# GPS Interference
gps_count = len(gps_data or [])
if gps_count:
drivers.append({
"signal": "GPS Interference",
"detail": f"{gps_count} active jamming zone{'s' if gps_count > 1 else ''} detected via ADS-B navigation anomalies",
"weight": "CRITICAL" if gps_count >= 3 else "HIGH",
"icon": "",
})
# Military Conflicts
if conflicts:
critical = sum(1 for c in conflicts if c.get("severity") == "CRITICAL")
high = sum(1 for c in conflicts if c.get("severity") == "HIGH")
active_regions = set(
_match_region(c.get("title", "")) for c in conflicts
if _match_region(c.get("title", ""))
)
if critical or high:
drivers.append({
"signal": "Military Activity",
"detail": f"{critical} CRITICAL, {high} HIGH severity events across {len(active_regions)} region{'s' if len(active_regions) != 1 else ''}",
"weight": "CRITICAL" if critical >= 5 else "HIGH" if critical >= 1 else "ELEVATED",
"icon": "",
})
# BGP Routing
bgp_status = str((bgp_data or {}).get("status", "STABLE")).upper()
if bgp_status in ("ELEVATED", "CRITICAL"):
updates = (bgp_data or {}).get("total_updates", 0)
drivers.append({
"signal": "BGP Routing",
"detail": f"Internet routing instability: {updates:,} updates/hour ({bgp_status})",
"weight": bgp_status,
"icon": "📡",
})
# Space Weather
sw = space_weather or {}
kp = sw.get("kp_index", 0)
if isinstance(kp, (int, float)) and kp >= 4:
drivers.append({
"signal": "Space Weather",
"detail": f"Geomagnetic storm Kp={kp:.1f}{sw.get('description', 'HF radio / satellite disruption risk')}",
"weight": "CRITICAL" if kp >= 7 else "HIGH" if kp >= 5 else "ELEVATED",
"icon": "",
})
# News keywords
keyword_drivers = [r for r in reasons if "×" in r]
if keyword_drivers:
top_kw = keyword_drivers[:3]
drivers.append({
"signal": "Intel Keywords",
"detail": f"High-weight terms in news: {', '.join(top_kw)}",
"weight": "ELEVATED" if score >= 8 else "GUARDED",
"icon": "📰",
})
# Military air movement
mil_count = sum(1 for p in (planes or []) if p.get("military"))
total_planes = len(planes or [])
if mil_count >= 5:
drivers.append({
"signal": "Military Air Movement",
"detail": f"{mil_count} military aircraft tracked globally ({total_planes:,} total airframes monitored)",
"weight": "HIGH" if mil_count >= 30 else "ELEVATED" if mil_count >= 15 else "GUARDED",
"icon": "",
})
# Maritime movement
ship_count = len(ships or [])
if ship_count >= 10:
tankers = sum(1 for s in (ships or []) if str(s.get("type", "")).lower() in ("tanker", "lng carrier"))
drivers.append({
"signal": "Maritime Activity",
"detail": f"{ship_count} vessels tracked globally" + (f" ({tankers} tankers near chokepoints)" if tankers >= 5 else ""),
"weight": "ELEVATED" if ship_count >= 100 else "GUARDED",
"icon": "🚢",
})
drivers.sort(key=lambda d: _WEIGHT_ORDER.get(d["weight"], 5))
return drivers[:6]
def _correlations(
gps_data: list, conflicts: list, bgp_data: dict, space_weather: dict,
) -> list[str]:
"""Find cross-domain signal correlations."""
out: list[str] = []
conflict_regions = set(
_match_region(c.get("title", "")) for c in (conflicts or [])
if _match_region(c.get("title", ""))
)
if gps_data and conflicts:
for z in gps_data:
glat, glon = z.get("lat", 0), z.get("lon", 0)
if 35 < glat < 55 and 25 < glon < 45 and "Eastern Europe" in conflict_regions:
out.append(
"GPS jamming in Eastern Europe coincides with active military operations "
"— likely electronic warfare activity"
)
break
for z in gps_data:
glat, glon = z.get("lat", 0), z.get("lon", 0)
if 25 < glat < 40 and 30 < glon < 50 and "Middle East" in conflict_regions:
out.append(
"GPS interference near Middle Eastern conflict zones suggests coordinated "
"electronic warfare alongside kinetic operations"
)
break
bgp_status = str((bgp_data or {}).get("status", "STABLE")).upper()
if bgp_status in ("ELEVATED", "CRITICAL") and conflicts:
out.append(
f"BGP routing instability ({bgp_status}) during active military operations "
"may indicate cyber operations or infrastructure targeting"
)
sw = space_weather or {}
kp = sw.get("kp_index", 0) if isinstance(sw.get("kp_index"), (int, float)) else 0
if kp >= 4 and gps_data:
out.append(
f"Elevated geomagnetic activity (Kp={kp:.1f}) may amplify GPS signal "
"degradation in existing interference zones"
)
if kp >= 5:
out.append(
f"Geomagnetic storm (Kp={kp:.1f}) affecting HF radio propagation — "
"military comms may shift to satellite links"
)
if not out:
out.append("No significant cross-domain signal correlations detected at this time")
return out[:4]
def _watch_items(conflicts: list, gps_data: list, space_weather: dict) -> list[str]:
"""Generate actionable intelligence watch items."""
items: list[str] = []
if conflicts:
critical = [c for c in conflicts if c.get("severity") == "CRITICAL"]
if critical:
regions: dict[str, list] = {}
for c in critical[:15]:
r = _match_region(c.get("title", "")) or "Global"
regions.setdefault(r, []).append(c)
for region, evts in sorted(regions.items(), key=lambda x: -len(x[1])):
items.append(
f"Monitor {region}: {len(evts)} critical event{'s' if len(evts) > 1 else ''} "
f"{evts[0].get('event_type', 'military activity')}"
)
if gps_data:
items.append(
f"Track {len(gps_data)} GPS interference zone{'s' if len(gps_data) > 1 else ''} "
"— assess impact on aviation safety and military navigation"
)
sw = space_weather or {}
if sw.get("alerts"):
items.append("Space weather alerts active — monitor satellite communication reliability")
if not items:
items.append("Maintain standard surveillance posture across all domains")
return items[:5]
def _threat_from_score(score: int) -> str:
if score >= 18:
return "SEVERE"
if score >= 13:
return "HIGH"
if score >= 8:
return "ELEVATED"
if score >= 4:
return "GUARDED"
return "LOW"
def _score_from_signals(news_items, gps_data, bgp_data) -> tuple[int, list[str]]:
score = 0
reasons: list[str] = []
titles = [str(item.get("title", "")).lower() for item in news_items[:20]]
title_blob = " ".join(titles)
for kw, weight in KEYWORD_SCORES.items():
hits = title_blob.count(kw)
if hits:
inc = min(hits, 3) * weight
score += inc
reasons.append(f"{kw}×{hits}")
gps_count = len(gps_data or [])
if gps_count:
bump = min(gps_count, 12)
score += bump
reasons.append(f"gps_anomalies={gps_count}")
bgp_status = str((bgp_data or {}).get("status", "STABLE")).upper()
if bgp_status == "ELEVATED":
score += 3
reasons.append("bgp=elevated")
elif bgp_status == "CRITICAL":
score += 6
reasons.append("bgp=critical")
elif bgp_status == "OFFLINE":
score += 2
reasons.append("bgp=offline")
return score, reasons
def _probability(score: int, offset: int) -> str:
pct = max(20, min(95, 35 + score * 3 + offset))
return f"{pct}%"
def _in_bbox(lat: float, lon: float, bbox: tuple) -> bool:
return bbox[0] <= lat <= bbox[2] and bbox[1] <= lon <= bbox[3]
def _heuristic_predictions(
news_items, gps_data, score: int,
conflicts=None, planes=None, ships=None,
) -> list[dict]:
"""
Generate predicted hotspots by cross-referencing:
- news keyword mentions (intel signal)
- conflict event severity (OSINT signal)
- military aircraft concentrations (movement signal)
- ship density in strategic chokepoints (maritime signal)
- GPS interference zones (EW signal)
"""
titles = [str(item.get("title", "")).lower() for item in (news_items or [])[:30]]
title_blob = " ".join(titles)
planes = planes or []
ships = ships or []
conflicts = conflicts or []
scored: list[tuple[float, str, dict]] = []
for key, region in HOTSPOT_REGIONS.items():
bbox = region["bbox"]
# ── 1. News keyword mentions ──
news_hits = sum(1 for kw in region["keywords"] if kw in title_blob)
news_score = news_hits * 3.0
# ── 2. Conflict event severity ──
conflict_crit = 0
conflict_high = 0
for c in conflicts:
ct = (c.get("title") or "").lower()
if any(kw in ct for kw in region["keywords"]):
sev = c.get("severity", "")
if sev == "CRITICAL":
conflict_crit += 1
elif sev == "HIGH":
conflict_high += 1
conflict_score = conflict_crit * 5.0 + conflict_high * 2.0
# ── 3. Military aircraft in region ──
mil_count = 0
civ_count = 0
for p in planes:
plat = p.get("lat")
plon = p.get("lon")
if isinstance(plat, (int, float)) and isinstance(plon, (int, float)):
if _in_bbox(plat, plon, bbox):
if p.get("military"):
mil_count += 1
else:
civ_count += 1
mil_score = min(mil_count * 2.0, 16.0)
# ── 4. Ship density near strategic chokepoints ──
ship_count = 0
tanker_count = 0
for s in ships:
slat = s.get("lat")
slon = s.get("lon")
if isinstance(slat, (int, float)) and isinstance(slon, (int, float)):
if _in_bbox(slat, slon, bbox):
ship_count += 1
if str(s.get("type", "")).lower() in ("tanker", "lng carrier"):
tanker_count += 1
ship_score = min(ship_count * 0.8 + tanker_count * 1.2, 10.0)
# ── 5. GPS interference overlap ──
gps_overlap = 0
for z in (gps_data or []):
zlat = z.get("lat", 0)
zlon = z.get("lon", 0)
if isinstance(zlat, (int, float)) and isinstance(zlon, (int, float)):
if _in_bbox(zlat, zlon, bbox):
gps_overlap += 1
ew_score = gps_overlap * 4.0
total = news_score + conflict_score + mil_score + ship_score + ew_score
if total < 1.0:
continue
# Build explanation string from active signals
signals = []
if news_hits:
signals.append(f"{news_hits} intel mentions")
if conflict_crit or conflict_high:
parts = []
if conflict_crit:
parts.append(f"{conflict_crit} CRITICAL")
if conflict_high:
parts.append(f"{conflict_high} HIGH")
signals.append(f"{'+'.join(parts)} events")
if mil_count:
signals.append(f"{mil_count} military aircraft tracked")
if ship_count:
desc = f"{ship_count} vessels"
if tanker_count:
desc += f" ({tanker_count} tankers)"
signals.append(desc)
if gps_overlap:
signals.append(f"{gps_overlap} GPS jamming zone{'s' if gps_overlap > 1 else ''}")
event = region["baseline"]
if signals:
event += "" + ", ".join(signals[:3])
prob_pct = max(25, min(95, int(30 + total * 2.5 + score * 1.5)))
# Build AI-style reasoning text from active signals
reason_parts = []
if news_hits:
matching_kws = [kw for kw in region["keywords"] if kw in title_blob][:3]
reason_parts.append(
f"Multiple intelligence feeds ({news_hits}) reference "
f"{', '.join(matching_kws) if matching_kws else 'this region'}, "
f"indicating heightened international attention."
)
if conflict_crit:
reason_parts.append(
f"{conflict_crit} CRITICAL-severity conflict event{'s' if conflict_crit > 1 else ''} "
f"detected via OSINT, suggesting active kinetic operations or imminent escalation."
)
if conflict_high:
reason_parts.append(
f"{conflict_high} HIGH-severity event{'s' if conflict_high > 1 else ''} "
f"reported in the area, indicating elevated security posture."
)
if mil_count:
reason_parts.append(
f"ADS-B tracking shows {mil_count} military aircraft operating within the region"
f"{f' alongside {civ_count} civilian flights' if civ_count > 20 else ''}, "
f"{'which represents unusual concentration' if mil_count >= 5 else 'consistent with ongoing monitoring'}."
)
if ship_count:
s_detail = f"{ship_count} vessels tracked"
if tanker_count:
s_detail += f" including {tanker_count} tanker{'s' if tanker_count > 1 else ''}"
reason_parts.append(
f"Maritime surveillance identifies {s_detail} in strategic waters, "
f"{'raising chokepoint disruption concerns' if tanker_count >= 2 else 'indicating normal commerce flow'}."
)
if gps_overlap:
reason_parts.append(
f"Active GPS jamming/spoofing detected ({gps_overlap} zone{'s' if gps_overlap > 1 else ''}), "
f"a strong indicator of electronic warfare activity in the area."
)
if not reason_parts:
reason_parts.append(region["baseline"])
reason = " ".join(reason_parts)
scored.append((total, key, {
"location": region["name"],
"lat": region["lat"],
"lon": region["lon"],
"event": event[:200],
"reason": reason[:500],
"probability": f"{prob_pct}%",
"_signals": {
"news": news_hits, "conflicts": conflict_crit + conflict_high,
"mil_planes": mil_count, "ships": ship_count, "gps": gps_overlap,
},
}))
scored.sort(key=lambda x: x[0], reverse=True)
predictions = []
for _, _, item in scored[:4]:
# Strip internal fields
item.pop("_signals", None)
predictions.append(item)
# Fallback if nothing ranked
if not predictions:
predictions.append({
"location": "Global Monitoring",
"lat": 20.0, "lon": 10.0,
"event": "No dominant hotspot; maintain broad surveillance",
"reason": "Current intelligence signals do not indicate a concentrated threat in any single region. Continuing broad-spectrum monitoring across all sensor feeds.",
"probability": _probability(score, 0),
})
return predictions
def _sanitize_prediction(item: dict, fallback_prob: str) -> dict | None:
if not isinstance(item, dict):
return None
lat = item.get("lat")
lon = item.get("lon")
try:
lat = float(lat)
lon = float(lon)
except Exception:
return None
if not (-90 <= lat <= 90 and -180 <= lon <= 180):
return None
location = str(item.get("location", "Unknown Location")).strip()[:80]
event = str(item.get("event", "Potential activity shift")).strip()[:180]
prob = str(item.get("probability", fallback_prob)).strip()[:8]
if not re.match(r"^\d{1,3}%$", prob):
prob = fallback_prob
return {
"location": location or "Unknown Location",
"lat": lat,
"lon": lon,
"event": event or "Potential activity shift",
"reason": str(item.get("reason", "")).strip()[:500],
"probability": prob,
}
def _sanitize_llm_output(raw: dict, fallback_level: str, fallback_predictions: list[dict]) -> dict:
summary = str(raw.get("summary", "")).strip()
if not summary:
summary = "AI produced no summary; heuristic assessment applied."
summary = summary[:220]
level = str(raw.get("threat_level", "")).upper().strip()
if level not in THREAT_LEVELS:
level = fallback_level
# Build lookup of heuristic reasons by location name for fallback
heuristic_reasons: dict[str, str] = {}
for fp in fallback_predictions:
loc = str(fp.get("location", "")).lower().strip()
if loc and fp.get("reason"):
heuristic_reasons[loc] = fp["reason"]
clean_predictions: list[dict] = []
for p in raw.get("predictions", [])[:4]:
cleaned = _sanitize_prediction(p, fallback_predictions[0]["probability"])
if cleaned:
# If LLM didn't provide a reason, try to merge from heuristic
if not cleaned.get("reason"):
loc_key = str(cleaned.get("location", "")).lower().strip()
cleaned["reason"] = heuristic_reasons.get(loc_key, "")
clean_predictions.append(cleaned)
if len(clean_predictions) >= 4:
break
if len(clean_predictions) < 2:
clean_predictions = fallback_predictions
return {
"summary": summary,
"threat_level": level,
"predictions": clean_predictions,
}
async def _query_ollama(prompt: str) -> dict | None:
async with httpx.AsyncClient() as client:
response = await client.post(
OLLAMA_URL,
json={
"model": MODEL_NAME,
"prompt": prompt,
"stream": False,
"format": "json",
},
timeout=20.0,
)
if response.status_code != 200:
return None
payload = response.json()
raw = payload.get("response", "")
if not raw:
return None
try:
parsed = json.loads(raw)
return parsed if isinstance(parsed, dict) else None
except Exception:
return None
async def analyze_threat(
news_items,
gps_data=None,
bgp_data=None,
conflicts=None,
space_weather=None,
ships=None,
planes=None,
):
if not news_items and not conflicts:
return {
"summary": "Awaiting intelligence data...",
"threat_level": "LOW",
"predictions": [],
"key_drivers": [],
"regional_briefs": [],
"correlations": [],
"watch_items": ["Maintain standard surveillance posture across all domains"],
"source": "heuristic",
"generated_at": datetime.now(timezone.utc).isoformat(),
}
score, reasons = _score_from_signals(news_items, gps_data or [], bgp_data or {})
# Boost score from conflicts
if conflicts:
crit = sum(1 for c in conflicts if c.get("severity") == "CRITICAL")
high = sum(1 for c in conflicts if c.get("severity") == "HIGH")
conflict_bump = min(crit * 2 + high, 10)
score += conflict_bump
if conflict_bump:
reasons.append(f"conflicts={crit}C/{high}H")
base_level = _threat_from_score(score)
base_predictions = _heuristic_predictions(
news_items, gps_data or [], score,
conflicts=conflicts or [],
planes=planes or [],
ships=ships or [],
)
# Build enhanced intel sections
regional = _regional_briefs(news_items, conflicts or [])
drivers = _key_drivers(score, reasons, gps_data or [], bgp_data or {}, conflicts or [], space_weather or {}, planes=planes, ships=ships)
corr = _correlations(gps_data or [], conflicts or [], bgp_data or {}, space_weather or {})
watch = _watch_items(conflicts or [], gps_data or [], space_weather or {})
# Build movement summary for LLM prompt
mil_planes_total = sum(1 for p in (planes or []) if p.get("military"))
ship_total = len(ships or [])
movement_ctx = (
f"Military aircraft tracked: {mil_planes_total}. "
f"Vessels tracked: {ship_total}. "
)
# Summarize heuristic predictions for LLM context
hotspot_ctx = " | ".join(
f"{p['location']}({p['probability']}): {p['event'][:60]}"
for p in base_predictions[:3]
)
headlines = " | ".join(str(item.get('title', ''))[:80] for item in news_items[:6])
prompt = (
f"STRATCOM AI. Strict JSON only, no markdown.\n"
f"News: {headlines}\n"
f"Signals: GPS={len(gps_data or [])}, BGP={(bgp_data or {}).get('status','?')}, {movement_ctx.strip()}\n"
f"Hotspots: {hotspot_ctx}\n"
f'Return: {{"summary":"<1 sentence>","threat_level":"LOW|GUARDED|ELEVATED|HIGH|SEVERE",'
f'"predictions":[{{"location":"","lat":0,"lon":0,"event":"","probability":"NN%","reason":""}}]}}'
)
try:
if _CLI_BACKEND != "ollama":
llm_raw = await _query_cli(prompt)
source_name = _CLI_BACKEND
else:
llm_raw = await _query_ollama(prompt)
source_name = MODEL_NAME
if llm_raw:
sanitized = _sanitize_llm_output(llm_raw, base_level, base_predictions)
sanitized["source"] = source_name
sanitized["generated_at"] = datetime.now(timezone.utc).isoformat()
sanitized["key_drivers"] = drivers
sanitized["regional_briefs"] = regional
sanitized["correlations"] = corr
sanitized["watch_items"] = watch
return sanitized
except Exception as exc:
print(f"[AI] LLM path failed ({_CLI_BACKEND}), fallback engaged: {exc}")
summary = f"{base_level} risk posture based on {len(news_items)} intelligence items"
if conflicts:
summary += f" + {len(conflicts)} military events"
if reasons:
summary += f"; key drivers: {', '.join(reasons[:4])}."
else:
summary += "."
return {
"summary": summary[:220],
"threat_level": base_level,
"predictions": base_predictions,
"key_drivers": drivers,
"regional_briefs": regional,
"correlations": corr,
"watch_items": watch,
"source": "heuristic",
"generated_at": datetime.now(timezone.utc).isoformat(),
}
if __name__ == "__main__":
TEST = [{"title": "Missile strike reported near border region"}, {"title": "Cyberattack impacts telecom routing"}]
print(asyncio.run(analyze_threat(TEST, gps_data=[{"lat": 32.1, "lon": 35.0}], bgp_data={"status": "ELEVATED"})))