atlasbot: use history for subjective follow-ups
This commit is contained in:
parent
e05a949b9f
commit
113bcdeded
@ -191,6 +191,10 @@ _INSIGHT_HINT_WORDS = {
|
||||
"cool",
|
||||
"unique",
|
||||
"notable",
|
||||
"coolest",
|
||||
"favorite",
|
||||
"favourite",
|
||||
"trivia",
|
||||
}
|
||||
|
||||
_OVERVIEW_HINT_WORDS = {
|
||||
@ -1550,6 +1554,21 @@ def _is_insight_query(query: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _is_subjective_query(query: str) -> bool:
|
||||
q = normalize_query(query)
|
||||
if not q:
|
||||
return False
|
||||
return any(word in q for word in _INSIGHT_HINT_WORDS) or any(
|
||||
phrase in q
|
||||
for phrase in (
|
||||
"what do you think",
|
||||
"your favorite",
|
||||
"your favourite",
|
||||
"your opinion",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _is_overview_query(query: str) -> bool:
|
||||
q = normalize_query(query)
|
||||
if not q:
|
||||
@ -1602,9 +1621,9 @@ def _insight_candidates(
|
||||
if postgres_line:
|
||||
candidates.append(("postgres", postgres_line, "high"))
|
||||
|
||||
hardware_line = _hardware_mix_line(inventory)
|
||||
if hardware_line:
|
||||
candidates.append(("hardware", hardware_line, "medium"))
|
||||
hardware_insight = _hardware_insight(inventory)
|
||||
if hardware_insight:
|
||||
candidates.append(("hardware", hardware_insight, "medium"))
|
||||
|
||||
pods_line = _pods_summary_line(metrics)
|
||||
if pods_line:
|
||||
@ -1613,6 +1632,29 @@ def _insight_candidates(
|
||||
return candidates
|
||||
|
||||
|
||||
def _hardware_insight(inventory: list[dict[str, Any]]) -> str:
|
||||
if not inventory:
|
||||
return ""
|
||||
groups = _group_nodes(inventory)
|
||||
jetsons = groups.get("jetson") or []
|
||||
rpi5 = groups.get("rpi5") or []
|
||||
rpi4 = groups.get("rpi4") or []
|
||||
amd64 = groups.get("amd64") or []
|
||||
if jetsons:
|
||||
jetson_names = ", ".join(jetsons[:2])
|
||||
return (
|
||||
f"Atlas mixes tiny Raspberry Pi nodes with Jetson accelerators ({jetson_names}) "
|
||||
f"and AMD64 servers, which is unusual for a homelab cluster."
|
||||
)
|
||||
if amd64 and (rpi5 or rpi4):
|
||||
return (
|
||||
"Atlas mixes small ARM boards with a couple of AMD64 machines, "
|
||||
"so workloads can land on either low-power or high-power nodes."
|
||||
)
|
||||
line = _hardware_mix_line(inventory)
|
||||
return line.replace("Hardware mix includes ", "Atlas mixes ") if line else ""
|
||||
|
||||
|
||||
def _select_insight(
|
||||
prompt: str,
|
||||
candidates: list[tuple[str, str, str]],
|
||||
@ -1623,6 +1665,8 @@ def _select_insight(
|
||||
prefer_keys: list[str] = []
|
||||
if any(word in q for word in ("unconventional", "weird", "odd", "unique", "surprising")):
|
||||
prefer_keys.extend(["hardware", "availability"])
|
||||
if any(word in q for word in ("coolest", "favorite", "favourite", "trivia", "fun")):
|
||||
prefer_keys.extend(["hardware", "cpu", "ram"])
|
||||
if any(word in q for word in ("another", "else", "different", "other")) and len(candidates) > 1:
|
||||
return candidates[1]
|
||||
if prefer_keys:
|
||||
@ -2284,7 +2328,24 @@ class _AtlasbotHandler(BaseHTTPRequestHandler):
|
||||
snapshot = _snapshot_state()
|
||||
inventory = _snapshot_inventory(snapshot) or node_inventory_live()
|
||||
workloads = _snapshot_workloads(snapshot)
|
||||
cluster_query = _is_cluster_query(cleaned, inventory=inventory, workloads=workloads)
|
||||
history_payload = payload.get("history") or []
|
||||
history_lines: list[str] = []
|
||||
if isinstance(history_payload, list):
|
||||
for item in history_payload[-10:]:
|
||||
if isinstance(item, dict):
|
||||
content = item.get("content") or item.get("message") or ""
|
||||
if isinstance(content, str) and content.strip():
|
||||
history_lines.append(content.strip())
|
||||
elif isinstance(item, str) and item.strip():
|
||||
history_lines.append(item.strip())
|
||||
history_cluster = _history_mentions_cluster(
|
||||
history_lines,
|
||||
inventory=inventory,
|
||||
workloads=workloads,
|
||||
)
|
||||
cluster_query = _is_cluster_query(cleaned, inventory=inventory, workloads=workloads) or (
|
||||
_is_subjective_query(cleaned) and history_cluster
|
||||
)
|
||||
context = ""
|
||||
if cluster_query:
|
||||
context = build_context(
|
||||
@ -2329,6 +2390,22 @@ history = collections.defaultdict(list) # (room_id, sender|None) -> list[str] (
|
||||
def key_for(room_id: str, sender: str, is_dm: bool):
|
||||
return (room_id, None) if is_dm else (room_id, sender)
|
||||
|
||||
|
||||
def _history_mentions_cluster(
|
||||
history_lines: list[str],
|
||||
*,
|
||||
inventory: list[dict[str, Any]] | None = None,
|
||||
workloads: list[dict[str, Any]] | None = None,
|
||||
) -> bool:
|
||||
recent = [line for line in history_lines[-8:] if isinstance(line, str)]
|
||||
for line in recent:
|
||||
cleaned = normalize_query(line)
|
||||
if not cleaned:
|
||||
continue
|
||||
if _is_cluster_query(cleaned, inventory=inventory, workloads=workloads):
|
||||
return True
|
||||
return False
|
||||
|
||||
def build_context(
|
||||
prompt: str,
|
||||
*,
|
||||
@ -2734,7 +2811,14 @@ def sync_loop(token: str, room_id: str):
|
||||
if not inventory:
|
||||
inventory = _snapshot_inventory(snapshot)
|
||||
workloads = _snapshot_workloads(snapshot)
|
||||
cluster_query = _is_cluster_query(cleaned_body, inventory=inventory, workloads=workloads)
|
||||
history_cluster = _history_mentions_cluster(
|
||||
history[hist_key],
|
||||
inventory=inventory,
|
||||
workloads=workloads,
|
||||
)
|
||||
cluster_query = _is_cluster_query(cleaned_body, inventory=inventory, workloads=workloads) or (
|
||||
_is_subjective_query(cleaned_body) and history_cluster
|
||||
)
|
||||
context = ""
|
||||
if cluster_query:
|
||||
context = build_context(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user