From 88426622395c25dfb033d1d01ef40c80f561e795 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 27 Jan 2026 19:13:31 -0300 Subject: [PATCH] atlasbot: refine node and postgres query handling --- services/comms/atlasbot-deployment.yaml | 2 +- services/comms/scripts/atlasbot/bot.py | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/services/comms/atlasbot-deployment.yaml b/services/comms/atlasbot-deployment.yaml index 72503b8..e1ff2bb 100644 --- a/services/comms/atlasbot-deployment.yaml +++ b/services/comms/atlasbot-deployment.yaml @@ -16,7 +16,7 @@ spec: labels: app: atlasbot annotations: - checksum/atlasbot-configmap: manual-atlasbot-65 + checksum/atlasbot-configmap: manual-atlasbot-66 vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: "comms" vault.hashicorp.com/agent-inject-secret-turn-secret: "kv/data/atlas/comms/turn-shared-secret" diff --git a/services/comms/scripts/atlasbot/bot.py b/services/comms/scripts/atlasbot/bot.py index 2b3657a..abdcbf2 100644 --- a/services/comms/scripts/atlasbot/bot.py +++ b/services/comms/scripts/atlasbot/bot.py @@ -538,7 +538,17 @@ def _detect_metric(q: str) -> str | None: def _detect_hardware_filters(q: str) -> tuple[set[str], set[str]]: include: set[str] = set() exclude: set[str] = set() - rpi_specific = "rpi4" in q or "rpi5" in q + rpi_specific = any( + phrase in q + for phrase in ( + "rpi4", + "rpi5", + "raspberry pi 4", + "raspberry pi 5", + "raspberry pi-4", + "raspberry pi-5", + ) + ) for hardware, phrases in HARDWARE_HINTS.items(): if hardware == "rpi" and rpi_specific: continue @@ -1226,7 +1236,11 @@ def snapshot_metric_answer( hottest = postgres.get("hottest_db") if isinstance(postgres.get("hottest_db"), dict) else {} parts: list[str] = [] if used is not None and max_conn is not None: - parts.append(f"Postgres connections: {used:.0f} used / {max_conn:.0f} max.") + free = max_conn - used + if any(word in q for word in ("free", "available", "remaining")): + parts.append(f"Postgres connections: {used:.0f} used / {max_conn:.0f} max ({free:.0f} free).") + else: + parts.append(f"Postgres connections: {used:.0f} used / {max_conn:.0f} max.") if hottest.get("label"): hot_val = hottest.get("value") hot_val_str = _format_metric_value(str(hot_val), percent=False) if hot_val is not None else "" @@ -1303,6 +1317,11 @@ def structured_answer( if not op and entity == "node": op = "list" if (include_hw or exclude_hw or nodes_in_query) else "count" + if entity == "node" and "total" in q and "ready" in q: + summary = _nodes_summary_line(inventory, snapshot) + if summary: + return _format_confidence(summary, "high") + if entity == "node" and ("hardware mix" in q or "architecture" in q): hw_line = _hardware_mix_line(inventory) if hw_line: