Remove 4-run rule and enable direct Hermes anomaly reasoning in Server.py
This commit is contained in:
+11
-19
@@ -37,7 +37,7 @@ except ImportError:
|
|||||||
CONFIG_FILE_NAME = "server_config.json"
|
CONFIG_FILE_NAME = "server_config.json"
|
||||||
DEFAULT_DB_FILE = "logar_state.db"
|
DEFAULT_DB_FILE = "logar_state.db"
|
||||||
EVALUATION_WINDOW_HOURS = 12
|
EVALUATION_WINDOW_HOURS = 12
|
||||||
RUN_THRESHOLD = 4
|
RUN_THRESHOLD = 1
|
||||||
|
|
||||||
app = FastAPI(title="LOGAR Cloud Ingestion & Hermes Hub", version="2.0.1")
|
app = FastAPI(title="LOGAR Cloud Ingestion & Hermes Hub", version="2.0.1")
|
||||||
|
|
||||||
@@ -260,8 +260,8 @@ def export_license_request_lic(config: Dict[str, Any]) -> Tuple[Dict[str, Any],
|
|||||||
|
|
||||||
def process_ingested_logs(payload: Dict[str, Any], db_path: str, window_hours: int, min_runs: int) -> Dict[str, Any]:
|
def process_ingested_logs(payload: Dict[str, Any], db_path: str, window_hours: int, min_runs: int) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Evaluates candidate issues against the 12-hour evaluation window and 4-run rule.
|
Evaluates ingested log anomalies and updates temporal run tracking.
|
||||||
Zero-state clients send raw candidate entries; this engine handles temporal state.
|
All anomalies are promoted directly to VERIFIED for Hermes AI reasoning and decision-making.
|
||||||
"""
|
"""
|
||||||
client_server = payload.get("server", "unknown-host")
|
client_server = payload.get("server", "unknown-host")
|
||||||
site_name = payload.get("site_name") or (client_server.split(".", 1)[1] if "." in client_server else "default")
|
site_name = payload.get("site_name") or (client_server.split(".", 1)[1] if "." in client_server else "default")
|
||||||
@@ -288,9 +288,6 @@ def process_ingested_logs(payload: Dict[str, Any], db_path: str, window_hours: i
|
|||||||
if severity in ["DEBUG", "TRACE"]:
|
if severity in ["DEBUG", "TRACE"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Errors are always passed immediately; the 4-run rule only concerns warnings
|
|
||||||
is_error = severity in ["ERROR", "CRITICAL", "FATAL"]
|
|
||||||
|
|
||||||
signature = log.get("signature", "unknown")
|
signature = log.get("signature", "unknown")
|
||||||
server = log.get("server", client_server)
|
server = log.get("server", client_server)
|
||||||
message = log.get("message", "")
|
message = log.get("message", "")
|
||||||
@@ -312,21 +309,17 @@ def process_ingested_logs(payload: Dict[str, Any], db_path: str, window_hours: i
|
|||||||
|
|
||||||
# 12-hour evaluation window expiry check
|
# 12-hour evaluation window expiry check
|
||||||
if (now - last_seen_dt) > timedelta(hours=window_hours):
|
if (now - last_seen_dt) > timedelta(hours=window_hours):
|
||||||
# Window elapsed: reset to new cycle
|
|
||||||
new_runs = 1
|
new_runs = 1
|
||||||
new_first_seen = now_iso
|
new_first_seen = now_iso
|
||||||
new_status = "VERIFIED" if is_error else "TRANSIENT"
|
|
||||||
else:
|
else:
|
||||||
# Same run guard: only increment count once per distinct run batch
|
|
||||||
if last_run_id != run_id:
|
if last_run_id != run_id:
|
||||||
new_runs = run_count + 1
|
new_runs = run_count + 1
|
||||||
else:
|
else:
|
||||||
new_runs = run_count
|
new_runs = run_count
|
||||||
new_first_seen = first_seen_str
|
new_first_seen = first_seen_str
|
||||||
# 4-run rule applies to warnings; errors are always passed immediately as VERIFIED
|
|
||||||
new_status = "VERIFIED" if (is_error or new_runs >= min_runs) else "TRANSIENT"
|
|
||||||
|
|
||||||
if new_status == "VERIFIED" and current_status != "VERIFIED":
|
new_status = "VERIFIED"
|
||||||
|
if current_status != "VERIFIED":
|
||||||
promoted_to_verified += 1
|
promoted_to_verified += 1
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
@@ -335,8 +328,7 @@ def process_ingested_logs(payload: Dict[str, Any], db_path: str, window_hours: i
|
|||||||
WHERE fingerprint = ?
|
WHERE fingerprint = ?
|
||||||
""", (new_runs, now_iso, new_first_seen, new_status, run_id, message, severity, fp))
|
""", (new_runs, now_iso, new_first_seen, new_status, run_id, message, severity, fp))
|
||||||
else:
|
else:
|
||||||
initial_status = "VERIFIED" if (is_error or 1 >= min_runs) else "TRANSIENT"
|
initial_status = "VERIFIED"
|
||||||
if initial_status == "VERIFIED":
|
|
||||||
promoted_to_verified += 1
|
promoted_to_verified += 1
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
INSERT INTO active_issues
|
INSERT INTO active_issues
|
||||||
@@ -548,7 +540,7 @@ async def handle_socket_client(reader: asyncio.StreamReader, writer: asyncio.Str
|
|||||||
if client_id and "server" not in log_payload:
|
if client_id and "server" not in log_payload:
|
||||||
log_payload["server"] = client_id
|
log_payload["server"] = client_id
|
||||||
|
|
||||||
# Ingest and apply 12h window / 4-run rule
|
# Ingest and evaluate anomalies with temporal run tracking
|
||||||
res = process_ingested_logs(
|
res = process_ingested_logs(
|
||||||
log_payload,
|
log_payload,
|
||||||
db_path=SERVER_STATE["config"]["db_path"],
|
db_path=SERVER_STATE["config"]["db_path"],
|
||||||
@@ -694,8 +686,8 @@ def list_clients():
|
|||||||
def get_verified_anomalies_for_hermes():
|
def get_verified_anomalies_for_hermes():
|
||||||
"""
|
"""
|
||||||
Ingestion endpoint for Hermes agentic workflows.
|
Ingestion endpoint for Hermes agentic workflows.
|
||||||
Returns only verified anomalies that have satisfied the 4-run persistence rule
|
Returns verified anomalies within the active 12-hour evaluation window
|
||||||
within the active 12-hour evaluation window. Transient blips (< 4 runs) are excluded.
|
for Hermes AI direct reasoning and autonomous decision-making.
|
||||||
"""
|
"""
|
||||||
db_path = SERVER_STATE["config"]["db_path"]
|
db_path = SERVER_STATE["config"]["db_path"]
|
||||||
conn = sqlite3.connect(db_path)
|
conn = sqlite3.connect(db_path)
|
||||||
@@ -1284,7 +1276,7 @@ def serve_webui():
|
|||||||
<span class="logo-icon">🛰️</span>
|
<span class="logo-icon">🛰️</span>
|
||||||
<div>
|
<div>
|
||||||
<h1>LOGAR Fleet Hub & License Management</h1>
|
<h1>LOGAR Fleet Hub & License Management</h1>
|
||||||
<div class="subtitle">Cloud Hub: <strong>__SERVER_NAME__</strong> | Evaluator: 12h Rolling Window & 4-Run Rule</div>
|
<div class="subtitle">Cloud Hub: <strong>__SERVER_NAME__</strong> | Evaluator: 12h Rolling Window & Direct Hermes Intelligence</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="system-tags">
|
<div class="system-tags">
|
||||||
@@ -1380,7 +1372,7 @@ def serve_webui():
|
|||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span class="card-title" style="font-size: 1rem;">Hermes Verified Anomalies (4-Run Rule & Errors)</span>
|
<span class="card-title" style="font-size: 1rem;">Hermes Verified Anomalies</span>
|
||||||
<span class="badge badge-active">Active Window: 12h</span>
|
<span class="badge badge-active">Active Window: 12h</span>
|
||||||
</div>
|
</div>
|
||||||
<table>
|
<table>
|
||||||
|
|||||||
Reference in New Issue
Block a user