From 600c124ef29ed45a3990dfaf386abb8827048b0d Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 27 Jan 2026 03:56:00 -0300 Subject: [PATCH] atlasbot: clarify scoped metrics and format percent values --- services/comms/scripts/atlasbot/bot.py | 57 ++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/services/comms/scripts/atlasbot/bot.py b/services/comms/scripts/atlasbot/bot.py index e6c7542..f8b3ccf 100644 --- a/services/comms/scripts/atlasbot/bot.py +++ b/services/comms/scripts/atlasbot/bot.py @@ -406,15 +406,56 @@ def _apply_node_filter(expr: str, node_regex: str | None) -> str: replacement = f'node_uname_info{{nodename!=\"\",nodename=~\"{node_regex}\"}}' return expr.replace(needle, replacement) +def _metric_expr_uses_percent(entry: dict[str, Any]) -> bool: + exprs = entry.get("exprs") + expr = exprs[0] if isinstance(exprs, list) and exprs else "" + return "* 100" in expr or "*100" in expr + + +def _format_metric_value(value: str, *, percent: bool) -> str: + try: + num = float(value) + except (TypeError, ValueError): + return value + if percent: + return f"{num:.1f}%" + if abs(num) >= 1: + return f"{num:.2f}".rstrip("0").rstrip(".") + return f"{num:.4f}".rstrip("0").rstrip(".") + + +def _format_metric_label(metric: dict[str, Any]) -> str: + label_parts = [] + for k in ("namespace", "pod", "container", "node", "instance", "job", "phase"): + if metric.get(k): + label_parts.append(f"{k}={metric.get(k)}") + if not label_parts: + for k in sorted(metric.keys()): + if k.startswith("__"): + continue + label_parts.append(f"{k}={metric.get(k)}") + if len(label_parts) >= 4: + break + return ", ".join(label_parts) if label_parts else "series" + + def _format_metric_answer(entry: dict[str, Any], res: dict | None) -> str: series = _vm_value_series(res) panel = entry.get("panel_title") or "Metric" if not series: return "" - rendered = vm_render_result(res, limit=5) - if not rendered: + percent = _metric_expr_uses_percent(entry) + lines: list[str] = [] + for r in series[:5]: + if not isinstance(r, dict): + continue + metric = r.get("metric") or {} + value = r.get("value") or [] + val = value[1] if isinstance(value, list) and len(value) > 1 else "" + label = _format_metric_label(metric if isinstance(metric, dict) else {}) + lines.append(f"{label}: {_format_metric_value(val, percent=percent)}") + if not lines: return "" - lines = [line.lstrip("-").strip() for line in rendered.splitlines() if line.strip().startswith("-")] if len(lines) == 1: return f"{panel}: {lines[0]}." return f"{panel}:\n" + "\n".join(f"- {line}" for line in lines) @@ -627,6 +668,16 @@ def structured_answer(prompt: str, *, inventory: list[dict[str, Any]], metrics_s res = vm_query(expr, timeout=20) answer = _format_metric_answer(entry, res) if answer: + scope_parts: list[str] = [] + if include_hw: + scope_parts.append(" and ".join(sorted(include_hw))) + if exclude_hw: + scope_parts.append(f"excluding {' and '.join(sorted(exclude_hw))}") + if only_workers: + scope_parts.append("worker") + if scope_parts: + scope = " ".join(scope_parts) + return f"Among {scope} nodes, {answer}" return answer if metrics_summary: return metrics_summary