Adds an optional plain-language summary and recommended action to every
active insight. A new Oban cron worker runs every 5 minutes, picks up
unenriched insights, and asks the configured LLM to restate the finding
and suggest one specific action grounded in the structured metadata.
- Migration: adds llm_summary, recommended_action, llm_model,
llm_enriched_at columns to preseem_insights
- Towerops.LLM context with swappable behaviour; real client uses Req
with the standard Req.Test plug for test isolation
- MockClient + InsightPrompt module (builds chat messages, parses JSON
with code-fence stripping and a raw-text fallback)
- Worker logs and skips on rate limits / missing key / parse errors so
insights still render without an AI summary when the LLM is unavailable
- Optional towerops-llm k8s secret with example template; deployment.yaml
references it as optional in both init and main containers
- UI renders summary + recommended action callout on /insights and on
the org-level insights page
Adds 8 tests targeting AgentChannel branches missed by existing tests:
- resolve_any_stuck_down_alerts: device_down and device_up stuck alerts
resolved on next successful poll
- handle_in result with :PING job_type (deprecated SnmpResult path used
by older agents) creates a monitoring check without crashing
- handle_live_poll_result with malformed job_id (missing reply_topic)
hits the warning catch-all branch
- build_mikrotik_job is included in the initial dispatch when the device
has a discovered SNMP profile whose manufacturer is MikroTik or whose
sys_descr mentions RouterOS, and excluded when mikrotik_enabled is false
- process_additional_polling_data path with extra OIDs runs without
crashing the channel
- Adds TcpExecutor receive timeout test using a server that accepts
but never responds, exercising recv timeout branch
- Adds Preseem.get_device_qoe_data tests covering both no-APs and
with-metrics paths through the AP query + metric formatting
Adds 4 tests that drive the nil → nil fallbacks in
load_single_sensor_chart_data, load_storage_chart_data,
load_interface_errors_chart_data, and load_interface_traffic_chart_data
by navigating to graph routes with non-existent IDs.
Adds direct call to MobileController.get_device with a snmp_device that
has interfaces — drives the Enum.map over interfaces in
format_equipment_details that previously had no fixture coverage.
Adds tests across five files:
- Towerops.Monitoring.CheckQuery (full coverage of all composable filter
fragments via inspect+Repo.all)
- ToweropsWeb.Admin.SecurityLive.Index change_filter event for both
permanent and temporary filter values
- Towerops.Agents.AgentCache cleanup_expired handle_info path that
prunes stale device entries while preserving :global_default
- ToweropsWeb.Api.V1.AgentReleaseWebhookController happy path
(notified/skipped/version) and error path (Codeberg 503)
- ToweropsWeb.DeviceLive.Index reorder_device / reorder_site
unauthorized branches via cross-org device/site fixtures
Adds an untagged loopback test (127.0.0.1) so System.cmd /
Task.async / Task.yield branches in run_ping/3 are covered in CI
runs that exclude :network-tagged tests.