fix ollama after profile change

This commit is contained in:
larssand
2026-07-06 12:24:10 +02:00
parent 9eb2448eb7
commit 6a0df3bc74
4 changed files with 57 additions and 5 deletions

View File

@@ -28,7 +28,7 @@ DEFAULT_CONFIG: dict[str, object] = {
"llm_model": "",
"profile_advisor_enabled": False,
"profile_advisor_model": "qwen3:8b",
"profile_advisor_timeout": 120,
"profile_advisor_timeout": 240,
"threat_intel_enabled": False,
"threat_intel_provider": "auto",
"abuseipdb_api_key": "",

View File

@@ -988,7 +988,7 @@ async function applySuggestedProfile(streamId) {
llm_model: config.llm_model || '',
profile_advisor_enabled: Boolean(config.profile_advisor_enabled),
profile_advisor_model: config.profile_advisor_model || 'qwen3:8b',
profile_advisor_timeout: config.profile_advisor_timeout || 120,
profile_advisor_timeout: config.profile_advisor_timeout || 240,
threat_intel_enabled: Boolean(config.threat_intel_enabled),
threat_intel_provider: config.threat_intel_provider || 'auto',
threat_intel_daily_limit: config.threat_intel_daily_limit || 100,

View File

@@ -129,7 +129,7 @@ def _compact_profile_suggestion(item: dict[str, object]) -> dict[str, object]:
def ollama_profile_advice(suggestions: list[dict[str, object]], model: str | None = None, timeout: int | None = None) -> list[dict[str, object]]:
host = os.getenv("OLLAMA_HOST", "http://127.0.0.1:11434").rstrip("/")
selected_model = model or os.getenv("FGAI_PROFILE_ADVISOR_MODEL", "qwen3:8b")
selected_timeout = timeout or int(os.getenv("FGAI_PROFILE_ADVISOR_TIMEOUT", "120"))
selected_timeout = timeout or int(os.getenv("FGAI_PROFILE_ADVISOR_TIMEOUT", "240"))
compact = [_compact_profile_suggestion(item) for item in suggestions[:10]]
body = json.dumps(
{

View File

@@ -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)