|
|
|
|
@@ -219,6 +219,7 @@ def build_status(
|
|
|
|
|
history_path: str | None = None,
|
|
|
|
|
incident_path: str | None = None,
|
|
|
|
|
status_cache_path: str | None = None,
|
|
|
|
|
run_profile_advisor: bool = True,
|
|
|
|
|
) -> dict[str, object]:
|
|
|
|
|
config_store = ConfigStore(config_path) if config_path else None
|
|
|
|
|
config_exists = bool(config_store and config_store.path.exists())
|
|
|
|
|
@@ -420,14 +421,16 @@ def build_status(
|
|
|
|
|
profile_suggestions = suggest_stream_profiles([*discovery_cache_events, *events], existing_profiles=stream_profiles)
|
|
|
|
|
profile_advisor_status = {"enabled": bool(runtime_values.get("profile_advisor_enabled")), "status": "disabled"}
|
|
|
|
|
advisor_candidates = [item for item in profile_suggestions if _needs_profile_advisor(item, stream_profiles)]
|
|
|
|
|
if runtime_values.get("profile_advisor_enabled") and profile_suggestions and not advisor_candidates:
|
|
|
|
|
if runtime_values.get("profile_advisor_enabled") and not run_profile_advisor:
|
|
|
|
|
profile_advisor_status = {"enabled": True, "status": "pending" if advisor_candidates else "skipped_no_profile_changes", "candidates": len(advisor_candidates)}
|
|
|
|
|
elif runtime_values.get("profile_advisor_enabled") and profile_suggestions and not advisor_candidates:
|
|
|
|
|
profile_advisor_status = {"enabled": True, "status": "skipped_no_profile_changes", "reason": "No missing profiles or newly discovered profile fields need advisor review."}
|
|
|
|
|
elif runtime_values.get("profile_advisor_enabled") and profile_suggestions:
|
|
|
|
|
try:
|
|
|
|
|
advice = ollama_profile_advice(
|
|
|
|
|
advisor_candidates[:10],
|
|
|
|
|
model=str(runtime_values.get("profile_advisor_model", "") or "qwen3:8b"),
|
|
|
|
|
timeout=int(runtime_values.get("profile_advisor_timeout", 120) or 120),
|
|
|
|
|
timeout=int(runtime_values.get("profile_advisor_timeout", 240) or 240),
|
|
|
|
|
)
|
|
|
|
|
profile_suggestions = apply_profile_advice(profile_suggestions, advice)
|
|
|
|
|
profile_advisor_status = {"enabled": True, "status": "ok" if advice else "empty", "profiles_returned": len(advice), "model": str(runtime_values.get("profile_advisor_model", "") or "qwen3:8b")}
|
|
|
|
|
@@ -605,6 +608,42 @@ def add_llm_assessment(status: dict[str, object], *, previous: str | None = None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_profile_advisor(status: dict[str, object], runtime_values: dict[str, object]) -> None:
|
|
|
|
|
profile_suggestions = status.get("profile_suggestions", [])
|
|
|
|
|
if not isinstance(profile_suggestions, list):
|
|
|
|
|
return
|
|
|
|
|
stream_profiles = parse_profiles(runtime_values.get("graylog_stream_profiles", []))
|
|
|
|
|
advisor_candidates = [item for item in profile_suggestions if isinstance(item, dict) and _needs_profile_advisor(item, stream_profiles)]
|
|
|
|
|
capabilities = status.setdefault("capabilities", {})
|
|
|
|
|
if not isinstance(capabilities, dict):
|
|
|
|
|
return
|
|
|
|
|
if not runtime_values.get("profile_advisor_enabled"):
|
|
|
|
|
capabilities["profile_advisor"] = {"enabled": False, "status": "disabled"}
|
|
|
|
|
return
|
|
|
|
|
if not profile_suggestions or not advisor_candidates:
|
|
|
|
|
capabilities["profile_advisor"] = {"enabled": True, "status": "skipped_no_profile_changes", "reason": "No missing profiles or newly discovered profile fields need advisor review."}
|
|
|
|
|
return
|
|
|
|
|
try:
|
|
|
|
|
advice = ollama_profile_advice(
|
|
|
|
|
advisor_candidates[:10],
|
|
|
|
|
model=str(runtime_values.get("profile_advisor_model", "") or "qwen3:8b"),
|
|
|
|
|
timeout=int(runtime_values.get("profile_advisor_timeout", 240) or 240),
|
|
|
|
|
)
|
|
|
|
|
status["profile_suggestions"] = apply_profile_advice(profile_suggestions, advice)
|
|
|
|
|
capabilities["profile_advisor"] = {"enabled": True, "status": "ok" if advice else "empty", "profiles_returned": len(advice), "model": str(runtime_values.get("profile_advisor_model", "") or "qwen3:8b")}
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
capabilities["profile_advisor"] = {
|
|
|
|
|
"enabled": True,
|
|
|
|
|
"status": "heuristic_fallback",
|
|
|
|
|
"error": str(exc),
|
|
|
|
|
"model": str(runtime_values.get("profile_advisor_model", "") or "qwen3:8b"),
|
|
|
|
|
"candidates": len(advisor_candidates),
|
|
|
|
|
}
|
|
|
|
|
for suggestion in profile_suggestions:
|
|
|
|
|
if isinstance(suggestion, dict):
|
|
|
|
|
suggestion.setdefault("profile_advisor", {"status": "heuristic", "error": str(exc)})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_status(status: dict[str, object], output: str) -> None:
|
|
|
|
|
output_path = Path(output)
|
|
|
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
@@ -703,6 +742,7 @@ def monitor_loop(
|
|
|
|
|
log_path, policy_path=policy_path, anomaly_limit=anomaly_limit,
|
|
|
|
|
baseline_path=baseline_path, config_path=config_path, history_path=history_path,
|
|
|
|
|
status_cache_path=status_cache_path,
|
|
|
|
|
run_profile_advisor=False,
|
|
|
|
|
)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
error_status = {"status": "error", "error": f"monitor_error: {exc}", "call_timeout_seconds": mcp_call_timeout, "poll_timeout_seconds": mcp_poll_timeout}
|
|
|
|
|
@@ -722,8 +762,15 @@ def monitor_loop(
|
|
|
|
|
cached = cached_status_with_error(status_cache_path, mcp)
|
|
|
|
|
if cached:
|
|
|
|
|
status = cached
|
|
|
|
|
status["llm_assessment"] = {"enabled": bool(effective_llm), "status": "cached" if last_llm_text else ("pending" if effective_llm else "disabled"), "generated_at": last_llm_at, "text": last_llm_text or ""}
|
|
|
|
|
if effective_llm:
|
|
|
|
|
if status_cache_path and isinstance(mcp, dict) and mcp.get("status") not in {"error", "refreshing"}:
|
|
|
|
|
StatusSnapshotStore(status_cache_path).save("last_good", status)
|
|
|
|
|
write_status(status, output)
|
|
|
|
|
now = int(time.time())
|
|
|
|
|
if runtime.get("profile_advisor_enabled"):
|
|
|
|
|
add_profile_advisor(status, runtime)
|
|
|
|
|
write_status(status, output)
|
|
|
|
|
if now - last_llm_at >= llm_interval:
|
|
|
|
|
add_llm_assessment(status, previous=last_llm_text, model=effective_model or None, timeout=llm_timeout)
|
|
|
|
|
assessment = status.get("llm_assessment", {})
|
|
|
|
|
@@ -739,6 +786,11 @@ def monitor_loop(
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
status["llm_assessment"] = {"enabled": False, "status": "disabled", "text": ""}
|
|
|
|
|
if runtime.get("profile_advisor_enabled"):
|
|
|
|
|
if status_cache_path and isinstance(mcp, dict) and mcp.get("status") not in {"error", "refreshing"}:
|
|
|
|
|
StatusSnapshotStore(status_cache_path).save("last_good", status)
|
|
|
|
|
write_status(status, output)
|
|
|
|
|
add_profile_advisor(status, runtime)
|
|
|
|
|
if status_cache_path and isinstance(mcp, dict) and mcp.get("status") not in {"error", "refreshing"}:
|
|
|
|
|
StatusSnapshotStore(status_cache_path).save("last_good", status)
|
|
|
|
|
write_status(status, output)
|
|
|
|
|
|