feat(insights): add 'AI' source filter for LLM-enriched insights
Adds an "ai" pill to the source-filter strip on /insights and a small blue "AI" badge on every card whose `llm_enriched_at` is set. Selecting the pill returns every LLM-enriched insight regardless of underlying source (preseem / snmp / gaiia / system). `source: "ai"` is a virtual filter — `list_insights/2` translates it to `WHERE llm_enriched_at IS NOT NULL` rather than equality on the source column, so existing real source semantics stay intact.
This commit is contained in:
parent
c9202744a5
commit
f6cd35d2d3
3 changed files with 67 additions and 2 deletions
|
|
@ -29,7 +29,16 @@ defmodule Towerops.Preseem.Insights do
|
|||
|
||||
query = if type, do: where(query, type: ^type), else: query
|
||||
query = if urgency, do: where(query, urgency: ^urgency), else: query
|
||||
query = if source, do: where(query, source: ^source), else: query
|
||||
|
||||
# `source: "ai"` is a virtual filter — surfaces every insight that has
|
||||
# been LLM-enriched, regardless of which underlying integration produced
|
||||
# it. Real source values (preseem/snmp/gaiia/system) match the column.
|
||||
query =
|
||||
case source do
|
||||
nil -> query
|
||||
"ai" -> where(query, [i], not is_nil(i.llm_enriched_at))
|
||||
s -> where(query, source: ^s)
|
||||
end
|
||||
|
||||
query
|
||||
|> preload([:preseem_access_point, :device])
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
{t("All")}
|
||||
</.link>
|
||||
<.link
|
||||
:for={source <- ~w(preseem gaiia snmp system)}
|
||||
:for={source <- ~w(preseem gaiia snmp system ai)}
|
||||
id={"filter-source-#{source}"}
|
||||
patch={
|
||||
~p"/insights?#{build_filter_params(%{status: @filter_status, urgency: @filter_urgency}, %{source: source})}"
|
||||
|
|
@ -258,6 +258,13 @@
|
|||
{insight.source}
|
||||
</span>
|
||||
|
||||
<%!-- AI badge — present on any LLM-enriched insight --%>
|
||||
<%= if insight.llm_enriched_at do %>
|
||||
<span class="inline-flex items-center rounded-full bg-blue-600 px-2 py-0.5 text-xs font-semibold text-white dark:bg-blue-500">
|
||||
AI
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%!-- Type badge --%>
|
||||
<span class="inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600 dark:bg-gray-700 dark:text-gray-300">
|
||||
{String.replace(insight.type, "_", " ")}
|
||||
|
|
|
|||
|
|
@ -354,6 +354,55 @@ defmodule Towerops.Preseem.InsightsTest do
|
|||
assert hd(snmp_insights).title == "SNMP insight"
|
||||
end
|
||||
|
||||
test "source: \"ai\" returns LLM-enriched insights regardless of underlying source",
|
||||
%{org: org, access_point: ap} do
|
||||
# Two unenriched insights from two different sources
|
||||
{:ok, _preseem_unenriched} =
|
||||
insert_insight(org, ap, %{
|
||||
type: "qoe_degradation",
|
||||
urgency: "warning",
|
||||
title: "Preseem (no LLM)"
|
||||
})
|
||||
|
||||
{:ok, _snmp_unenriched} =
|
||||
insert_insight_with_source(org, %{
|
||||
type: "snmp_cpu_high",
|
||||
urgency: "warning",
|
||||
title: "SNMP (no LLM)",
|
||||
source: "snmp"
|
||||
})
|
||||
|
||||
# Two enriched insights from two different sources — these should match.
|
||||
{:ok, _preseem_enriched} =
|
||||
insert_insight(org, ap, %{
|
||||
type: "qoe_degradation",
|
||||
urgency: "warning",
|
||||
title: "Preseem (enriched)",
|
||||
llm_summary: "summary",
|
||||
llm_model: "deepseek-v4-pro",
|
||||
llm_enriched_at: Towerops.Time.now()
|
||||
})
|
||||
|
||||
{:ok, _snmp_enriched} =
|
||||
insert_insight_with_source(org, %{
|
||||
type: "snmp_cpu_high",
|
||||
urgency: "warning",
|
||||
title: "SNMP (enriched)",
|
||||
source: "snmp",
|
||||
llm_summary: "summary",
|
||||
llm_model: "deepseek-v4-pro",
|
||||
llm_enriched_at: Towerops.Time.now()
|
||||
})
|
||||
|
||||
ai_insights = Insights.list_insights(org.id, source: "ai")
|
||||
titles = Enum.map(ai_insights, & &1.title)
|
||||
|
||||
assert "Preseem (enriched)" in titles
|
||||
assert "SNMP (enriched)" in titles
|
||||
refute "Preseem (no LLM)" in titles
|
||||
refute "SNMP (no LLM)" in titles
|
||||
end
|
||||
|
||||
test "returns all sources when no source filter", %{org: org, access_point: ap} do
|
||||
insert_insight(org, ap, %{type: "qoe_degradation", urgency: "warning", title: "Preseem"})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue