Files
Shadowbroker/backend/analytics/settings.py
T
Shadowbroker cfbeabda1e Feat/gt analytics openclaw (#392)
* feat(telegram): auto-translate OSINT channel posts to English

Cherry-picked from @Bobpick PR #391 (telegram-only slice): server-side translation during fetch, SHOW ORIGINAL toggle in TelegramOsintPopup, and on-demand /api/telegram-feed?lang=.

Co-authored-by: Robert Pickett <bobpickettsr@yahoo.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(gt): experimental Derived OSINT analytics with lean-node safeguards

Cherry-picked from @Bobpick PR #391 (GT + OpenClaw slice): Bayesian strategic-risk engine, map overlay, OpenClaw commands, and telegram_rhetoric watchdog. Off by default (GT_ANALYTICS_ENABLED=false, gt_risk layer false). 1 vCPU nodes get cgroup detection, UI warning on layer toggle, and lean profile that skips scheduled ingest/Louvain unless GT_ANALYTICS_ACK_LOW_CPU=true. Backtest HUD removed from dashboard (OpenClaw/API regression only).

Co-authored-by: Robert Pickett <bobpickettsr@yahoo.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Robert Pickett <bobpickettsr@yahoo.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 17:05:46 -06:00

158 lines
4.8 KiB
Python

"""Configuration for Strategic Risk Analytics (feature-flagged)."""
from __future__ import annotations
import json
import os
from dataclasses import dataclass, field
from functools import lru_cache
from typing import Any
def _env_bool(name: str, default: bool = False) -> bool:
raw = str(os.environ.get(name, "")).strip().lower()
if not raw:
return default
return raw not in {"0", "false", "no", "off"}
def _env_float(name: str, default: float) -> float:
raw = str(os.environ.get(name, "")).strip()
if not raw:
return default
try:
return float(raw)
except ValueError:
return default
def _env_int(name: str, default: int) -> int:
raw = str(os.environ.get(name, "")).strip()
if not raw:
return default
try:
return int(raw)
except ValueError:
return default
def _parse_signal_weights(raw: str) -> dict[str, float]:
if not raw.strip():
return {}
try:
parsed = json.loads(raw)
if isinstance(parsed, dict):
return {str(k): float(v) for k, v in parsed.items()}
except (json.JSONDecodeError, TypeError, ValueError):
pass
weights: dict[str, float] = {}
for part in raw.split(","):
piece = part.strip()
if not piece or "=" not in piece:
continue
key, value = piece.split("=", 1)
try:
weights[key.strip()] = float(value.strip())
except ValueError:
continue
return weights
def resolve_gt_profile() -> str:
from services.runtime_profile import resolve_profile_name
return resolve_profile_name()
def gt_analytics_ack_low_cpu() -> bool:
return _env_bool("GT_ANALYTICS_ACK_LOW_CPU", default=False)
def gt_engine_operational() -> bool:
"""Full GT engine (scheduled ingest, heatmap, Louvain) — not watchdog-only."""
if not get_gt_settings().enabled:
return False
if resolve_gt_profile() == "lean" and not gt_analytics_ack_low_cpu():
return False
return True
def gt_scheduled_ingest_enabled() -> bool:
return gt_engine_operational()
def gt_louvain_enabled() -> bool:
return gt_engine_operational()
@dataclass(frozen=True)
class GTAnalyticsSettings:
enabled: bool = False
profile: str = "standard"
base_prior: float = 0.15
evidence_cap: float = 3.0
evidence_scale: float = 5.0
min_prob: float = 0.01
max_prob: float = 0.99
high_risk_threshold: float = 0.6
max_history_per_region: int = 200
max_heatmap_features: int = 500
louvain_min_weight: float = 0.5
louvain_interval_minutes: int = 30
signal_weight_overrides: dict[str, float] = field(default_factory=dict)
watched_channels: tuple[str, ...] = ()
@lru_cache(maxsize=1)
def get_gt_settings() -> GTAnalyticsSettings:
channels_raw = str(os.environ.get("GT_ANALYTICS_WATCHED_CHANNELS", "")).strip()
channels = tuple(
part.strip().lstrip("@")
for part in channels_raw.split(",")
if part.strip()
)
profile = resolve_gt_profile()
lean = profile == "lean"
return GTAnalyticsSettings(
enabled=_env_bool("GT_ANALYTICS_ENABLED", default=False),
profile=profile,
base_prior=_env_float("GT_ANALYTICS_BASE_PRIOR", 0.15),
evidence_cap=_env_float("GT_ANALYTICS_EVIDENCE_CAP", 3.0),
evidence_scale=_env_float("GT_ANALYTICS_EVIDENCE_SCALE", 5.0),
min_prob=_env_float("GT_ANALYTICS_MIN_PROB", 0.01),
max_prob=_env_float("GT_ANALYTICS_MAX_PROB", 0.99),
high_risk_threshold=_env_float("GT_ANALYTICS_HIGH_RISK_THRESHOLD", 0.6),
max_history_per_region=_env_int("GT_ANALYTICS_MAX_HISTORY", 200),
max_heatmap_features=_env_int(
"GT_ANALYTICS_MAX_HEATMAP_FEATURES",
50 if lean else 500,
),
louvain_min_weight=_env_float("GT_ANALYTICS_LOUVAIN_MIN_WEIGHT", 0.5),
louvain_interval_minutes=max(5, _env_int("GT_ANALYTICS_LOUVAIN_INTERVAL_MINUTES", 30)),
signal_weight_overrides=_parse_signal_weights(
str(os.environ.get("GT_ANALYTICS_SIGNAL_WEIGHTS", ""))
),
watched_channels=channels,
)
def gt_analytics_enabled() -> bool:
return get_gt_settings().enabled
def gt_analytics_status() -> dict[str, Any]:
settings = get_gt_settings()
from services.runtime_profile import get_runtime_profile
runtime = get_runtime_profile()
operational = gt_engine_operational()
return {
"enabled": settings.enabled,
"operational": operational,
"profile": settings.profile,
"ack_low_cpu": gt_analytics_ack_low_cpu(),
"recommended": bool(runtime.get("gt_analytics", {}).get("recommended")),
"lean_node": bool(runtime.get("gt_analytics", {}).get("lean_node")),
"warning": runtime.get("gt_analytics", {}).get("warning"),
"experimental": True,
}