atlasbot: simplify metric formatting and kb chunking

This commit is contained in:
Brad Stein 2026-02-04 16:05:41 -03:00
parent 4aec30c5c9
commit 109ae4e645
2 changed files with 100 additions and 61 deletions

View File

@ -2094,11 +2094,52 @@ def _format_direct_metric_line(line: str) -> str:
if not line: if not line:
return "" return ""
if ":" in line: if ":" in line:
formatted = _format_colon_metric(line)
if formatted:
return formatted
if "=" in line:
formatted = _format_equals_metric(line)
if formatted:
return formatted
return line
def _format_colon_metric(line: str) -> str | None:
key, value = line.split(":", 1) key, value = line.split(":", 1)
key = key.strip().replace("_", " ") key = key.strip().replace("_", " ")
value = value.strip() value = value.strip()
if value: if not value:
return None
if key == "nodes": if key == "nodes":
formatted = _format_nodes_value(value)
if formatted:
return formatted
if key in {"nodes total", "nodes_total"}:
return f"Atlas has {value} total nodes."
return f"{key} is {value}."
def _format_equals_metric(line: str) -> str | None:
pairs: list[str] = []
for part in line.split(","):
if "=" not in part:
continue
key, value = part.split("=", 1)
key = key.strip().replace("_", " ")
value = value.strip()
if not value:
continue
if key in {"nodes total", "nodes_total"}:
return f"Atlas has {value} total nodes."
pairs.append(f"{key} is {value}")
if not pairs:
return None
if len(pairs) == 1:
return f"{pairs[0]}."
return "; ".join(pairs) + "."
def _format_nodes_value(value: str) -> str | None:
parts = [p.strip() for p in value.split(",") if p.strip()] parts = [p.strip() for p in value.split(",") if p.strip()]
total = None total = None
rest: list[str] = [] rest: list[str] = []
@ -2107,31 +2148,11 @@ def _format_direct_metric_line(line: str) -> str:
total = part.split("=", 1)[1] total = part.split("=", 1)[1]
else: else:
rest.append(part.replace("_", " ")) rest.append(part.replace("_", " "))
if total: if not total:
return None
if rest: if rest:
return f"Atlas has {total} total nodes ({'; '.join(rest)})." return f"Atlas has {total} total nodes ({'; '.join(rest)})."
return f"Atlas has {total} total nodes." return f"Atlas has {total} total nodes."
if key in {"nodes total", "nodes_total"}:
return f"Atlas has {value} total nodes."
return f"{key} is {value}."
if "=" in line:
pairs: list[str] = []
for part in line.split(","):
if "=" not in part:
continue
k, v = part.split("=", 1)
k = k.strip().replace("_", " ")
v = v.strip()
if not v:
continue
if k in {"nodes total", "nodes_total"}:
return f"Atlas has {v} total nodes."
pairs.append(f"{k} is {v}")
if pairs:
if len(pairs) == 1:
return f"{pairs[0]}."
return "; ".join(pairs) + "."
return line
def _global_facts(lines: list[str]) -> list[str]: def _global_facts(lines: list[str]) -> list[str]:

View File

@ -78,21 +78,39 @@ class KnowledgeBase:
def chunk_lines(self, *, max_files: int = 20, max_chars: int = 6000) -> list[str]: def chunk_lines(self, *, max_files: int = 20, max_chars: int = 6000) -> list[str]:
self.load() self.load()
lines: list[str] = []
if not self._base: if not self._base:
return []
lines: list[str] = []
self._append_summary(lines)
self._append_catalog(lines, max_chars)
if not self._within_limit(lines, max_chars):
return lines return lines
self._append_runbooks(lines)
if not self._within_limit(lines, max_chars):
return lines
self._append_files(lines, max_files=max_files, max_chars=max_chars)
return lines
def _append_summary(self, lines: list[str]) -> None:
summary = self.summary() summary = self.summary()
if summary: if summary:
lines.append(f"KB Summary: {summary}") lines.append(f"KB Summary: {summary}")
# Prefer curated catalog JSON if present.
if self._atlas: def _append_catalog(self, lines: list[str], max_chars: int) -> None:
if not self._atlas:
return
if not self._within_limit(lines, max_chars):
return
try: try:
atlas_json = json.dumps(self._atlas, indent=2) atlas_json = json.dumps(self._atlas, indent=2)
except Exception:
return
lines.append("KB: atlas.json") lines.append("KB: atlas.json")
lines.extend(atlas_json.splitlines()) lines.extend(atlas_json.splitlines())
except Exception:
pass def _append_runbooks(self, lines: list[str]) -> None:
if self._runbooks: if not self._runbooks:
return
lines.append("KB: runbooks.json") lines.append("KB: runbooks.json")
for entry in self._runbooks: for entry in self._runbooks:
if not isinstance(entry, dict): if not isinstance(entry, dict):
@ -101,12 +119,11 @@ class KnowledgeBase:
path = entry.get("path") path = entry.get("path")
if title and path: if title and path:
lines.append(f"- {title} ({path})") lines.append(f"- {title} ({path})")
# Include markdown/text sources as additional chunks.
if len(lines) >= max_chars: def _append_files(self, lines: list[str], *, max_files: int, max_chars: int) -> None:
return lines
files = sorted(self._base.rglob("*.md")) + sorted(self._base.rglob("*.txt")) files = sorted(self._base.rglob("*.md")) + sorted(self._base.rglob("*.txt"))
for path in files: for path in files:
if len(lines) >= max_chars: if not self._within_limit(lines, max_chars):
break break
if len(lines) > max_files * 50: if len(lines) > max_files * 50:
break break
@ -118,6 +135,7 @@ class KnowledgeBase:
continue continue
lines.append(f"KB File: {path.relative_to(self._base)}") lines.append(f"KB File: {path.relative_to(self._base)}")
lines.extend(text.splitlines()) lines.extend(text.splitlines())
if sum(len(line) for line in lines) >= max_chars:
break @staticmethod
return lines def _within_limit(lines: list[str], max_chars: int) -> bool:
return sum(len(line) for line in lines) < max_chars