From 9c6c08b5ec430727393842f00bf4a9ae3c686b3e595ea700b78f65830adccf9d Mon Sep 17 00:00:00 2001 From: larssand Date: Tue, 30 Jun 2026 09:15:03 +0200 Subject: [PATCH] Added local Ollama model listing in the U --- README.md | 4 ++++ src/fgai/dashboard.py | 51 ++++++++++++++++++++++++++++++++++++++++- tests/test_dashboard.py | 28 ++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/test_dashboard.py diff --git a/README.md b/README.md index c0aa6bb..a85a604 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,10 @@ validated against fields actually seen in the stream before it can be applied. Unknown fields, raw message fields, internal `fgai_*` fields, and unknown detectors are rejected. +Settings also lists locally installed Ollama models from `http://127.0.0.1:11434/api/tags`. +Click a model name to fill both the dashboard analyst model and profile advisor +model fields. + The settings page treats stream enablement and profile editing separately. The checkboxes decide which streams are monitored. Click `Edit profile` on one stream to load its fields and edit only that stream's profile; saving with no active diff --git a/src/fgai/dashboard.py b/src/fgai/dashboard.py index ab0472e..6156ecd 100644 --- a/src/fgai/dashboard.py +++ b/src/fgai/dashboard.py @@ -3,6 +3,7 @@ from __future__ import annotations import json from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path +from urllib import error, request from urllib.parse import parse_qs, urlparse from .config import ConfigStore @@ -13,6 +14,30 @@ from .feedback import FeedbackStore from .incidents import IncidentStore +def _ollama_models(host: str = "http://127.0.0.1:11434") -> dict[str, object]: + try: + req = request.Request(f"{host.rstrip('/')}/api/tags", method="GET") + with request.urlopen(req, timeout=5) as response: + payload = json.loads(response.read().decode("utf-8")) + except error.URLError as exc: + return {"status": "error", "error": str(exc), "models": []} + except (json.JSONDecodeError, OSError) as exc: + return {"status": "error", "error": str(exc), "models": []} + models = payload.get("models", []) + if not isinstance(models, list): + models = [] + output = [] + for item in models: + if not isinstance(item, dict): + continue + output.append({ + "name": str(item.get("name", "")), + "modified_at": str(item.get("modified_at", "")), + "size": int(item.get("size", 0) or 0), + }) + return {"status": "ok", "models": sorted(output, key=lambda model: model["name"])} + + HTML = """ @@ -72,6 +97,8 @@ HTML = """ .graph { width: 100%; height: 300px; background: #04182d; border: 1px solid #163b59; } .sort-button { border: 0; background: transparent; color: #83bce9; cursor: pointer; font: inherit; font-weight: 600; padding: 0; } .sort-button:hover { color: #d9e8f7; } + .model-list { display: flex; flex-wrap: wrap; gap: 8px; } + .model-pill { border: 1px solid #39709a; background: #08243e; color: #d9e8f7; padding: 5px 8px; cursor: pointer; } @media (max-width: 860px) { .hero, .split { grid-template-columns: 1fr; } .hero img { display: none; } } @@ -89,7 +116,7 @@ HTML = """

Events and Anomalies

Baseline and Stream Health

Correlation Map

AI Assessment

LLM assessment disabled.

Investigation Incidents

Anomalies

Recommendations

Triage Queue

Field Baseline Deviations

Related Activity Across Sources

Block Candidates

Threat Intelligence

Policy Findings

Diagnostics

-

Recommended Stream Profiles

Waiting for observed stream data.

Runtime Configuration

+

Recommended Stream Profiles

Waiting for observed stream data.

Installed Ollama Models

Loading local Ollama models.

Runtime Configuration