quality(atlasbot): merge strict gate coverage

This commit is contained in:
jenkins 2026-04-21 00:56:41 -03:00
commit dd077b0f92
17 changed files with 1846 additions and 388 deletions

View File

@ -38,17 +38,7 @@ def _strip_followup_meta(reply: str) -> str:
return cleaned
def _build_meta(
mode: str,
call_count: int,
call_cap: int,
limit_hit: bool,
time_budget_hit: bool,
time_budget_sec: float,
classify: dict[str, Any],
tool_hint: dict[str, Any] | None,
started: float,
) -> dict[str, Any]:
def _build_meta(mode: str, call_count: int, call_cap: int, limit_hit: bool, time_budget_hit: bool, time_budget_sec: float, classify: dict[str, Any], tool_hint: dict[str, Any] | None, started: float) -> dict[str, Any]:
return {
"mode": mode,
"llm_calls": call_count,
@ -214,13 +204,7 @@ def _build_chunk_groups(chunks: list[dict[str, Any]], group_size: int) -> list[l
return groups
async def _score_chunks(
call_llm: Callable[..., Any],
chunks: list[dict[str, Any]],
question: str,
sub_questions: list[str],
plan: ModePlan,
) -> dict[str, float]:
async def _score_chunks(call_llm: Callable[..., Any], chunks: list[dict[str, Any]], question: str, sub_questions: list[str], plan: ModePlan) -> dict[str, float]:
scores: dict[str, float] = {chunk["id"]: 0.0 for chunk in chunks}
if not chunks:
return scores
@ -238,11 +222,7 @@ async def _score_chunks(
return await _score_groups_parallel(call_llm, groups, ctx)
async def _score_groups_serial(
call_llm: Callable[..., Any],
groups: list[list[dict[str, Any]]],
ctx: ScoreContext,
) -> dict[str, float]:
async def _score_groups_serial(call_llm: Callable[..., Any], groups: list[list[dict[str, Any]]], ctx: ScoreContext) -> dict[str, float]:
scores: dict[str, float] = {}
for grp in groups:
runs = [await _score_chunk_group(call_llm, grp, ctx.question, ctx.sub_questions) for _ in range(ctx.retries)]
@ -254,11 +234,7 @@ async def _score_groups_serial(
return scores
async def _score_groups_parallel(
call_llm: Callable[..., Any],
groups: list[list[dict[str, Any]]],
ctx: ScoreContext,
) -> dict[str, float]:
async def _score_groups_parallel(call_llm: Callable[..., Any], groups: list[list[dict[str, Any]]], ctx: ScoreContext) -> dict[str, float]:
coros: list[Awaitable[tuple[int, dict[str, float]]]] = []
for idx, grp in enumerate(groups):
for _ in range(ctx.retries):
@ -278,12 +254,7 @@ async def _score_groups_parallel(
return scores
async def _score_chunk_group(
call_llm: Callable[..., Any],
group: list[dict[str, Any]],
question: str,
sub_questions: list[str],
) -> dict[str, float]:
async def _score_chunk_group(call_llm: Callable[..., Any], group: list[dict[str, Any]], question: str, sub_questions: list[str]) -> dict[str, float]:
prompt = (
prompts.CHUNK_SCORE_PROMPT
+ "\nQuestion: "
@ -310,13 +281,7 @@ async def _score_chunk_group(
return scored
async def _score_chunk_group_run(
call_llm: Callable[..., Any],
idx: int,
group: list[dict[str, Any]],
question: str,
sub_questions: list[str],
) -> tuple[int, dict[str, float]]:
async def _score_chunk_group_run(call_llm: Callable[..., Any], idx: int, group: list[dict[str, Any]], question: str, sub_questions: list[str]) -> tuple[int, dict[str, float]]:
return idx, await _score_chunk_group(call_llm, group, question, sub_questions)
@ -332,12 +297,7 @@ def _merge_score_runs(runs: list[dict[str, float]]) -> dict[str, float]:
return {key: totals[key] / counts[key] for key in totals}
async def _select_best_score_run(
call_llm: Callable[..., Any],
group: list[dict[str, Any]],
runs: list[dict[str, float]],
ctx: ScoreContext,
) -> dict[str, float]:
async def _select_best_score_run(call_llm: Callable[..., Any], group: list[dict[str, Any]], runs: list[dict[str, float]], ctx: ScoreContext) -> dict[str, float]:
if not runs:
return {}
prompt = (
@ -364,11 +324,7 @@ async def _select_best_score_run(
return runs[idx]
def _keyword_hits(
ranked: list[dict[str, Any]],
head: dict[str, Any],
keywords: list[str] | None,
) -> list[dict[str, Any]]:
def _keyword_hits(ranked: list[dict[str, Any]], head: dict[str, Any], keywords: list[str] | None) -> list[dict[str, Any]]:
if not keywords:
return []
lowered = [kw.lower() for kw in keywords if isinstance(kw, str) and kw.strip()]
@ -384,13 +340,7 @@ def _keyword_hits(
return hits
def _select_chunks(
chunks: list[dict[str, Any]],
scores: dict[str, float],
plan: ModePlan,
keywords: list[str] | None = None,
must_ids: list[str] | None = None,
) -> list[dict[str, Any]]:
def _select_chunks(chunks: list[dict[str, Any]], scores: dict[str, float], plan: ModePlan, keywords: list[str] | None = None, must_ids: list[str] | None = None) -> list[dict[str, Any]]:
if not chunks:
return []
ranked = sorted(chunks, key=lambda item: scores.get(item["id"], 0.0), reverse=True)
@ -403,12 +353,7 @@ def _select_chunks(
return selected
def _append_must_chunks(
chunks: list[dict[str, Any]],
selected: list[dict[str, Any]],
must_ids: list[str] | None,
limit: int,
) -> bool:
def _append_must_chunks(chunks: list[dict[str, Any]], selected: list[dict[str, Any]], must_ids: list[str] | None, limit: int) -> bool:
if not must_ids:
return False
id_map = {item["id"]: item for item in chunks}
@ -421,12 +366,7 @@ def _append_must_chunks(
return False
def _append_keyword_chunks(
ranked: list[dict[str, Any]],
selected: list[dict[str, Any]],
keywords: list[str] | None,
limit: int,
) -> bool:
def _append_keyword_chunks(ranked: list[dict[str, Any]], selected: list[dict[str, Any]], keywords: list[str] | None, limit: int) -> bool:
if not ranked:
return False
head = ranked[0]
@ -438,11 +378,7 @@ def _append_keyword_chunks(
return False
def _append_ranked_chunks(
ranked: list[dict[str, Any]],
selected: list[dict[str, Any]],
limit: int,
) -> None:
def _append_ranked_chunks(ranked: list[dict[str, Any]], selected: list[dict[str, Any]], limit: int) -> None:
for item in ranked:
if len(selected) >= limit:
break

View File

@ -31,29 +31,14 @@ class AnswerEngine:
post-processing helpers stay split across smaller modules.
"""
def __init__(
self,
settings: Settings,
llm: LLMClient,
kb: KnowledgeBase,
snapshot: SnapshotProvider,
) -> None:
def __init__(self, settings: Settings, llm: LLMClient, kb: KnowledgeBase, snapshot: SnapshotProvider) -> None:
self._settings = settings
self._llm = llm
self._kb = kb
self._snapshot = snapshot
self._store = ClaimStore(settings.state_db_path, settings.conversation_ttl_sec)
async def answer(
self,
question: str,
*,
mode: str,
history: list[dict[str, str]] | None = None,
observer: Callable[[str, str], None] | None = None,
conversation_id: str | None = None,
snapshot_pin: bool | None = None,
) -> AnswerResult:
async def answer(self, question: str, *, mode: str, history: list[dict[str, str]] | None = None, observer: Callable[[str, str], None] | None = None, conversation_id: str | None = None, snapshot_pin: bool | None = None) -> AnswerResult:
"""Answer a question by delegating to the staged workflow."""
return await run_answer(
@ -71,15 +56,7 @@ class AnswerEngine:
reply = await self._llm.chat(messages, model=self._settings.ollama_model)
return AnswerResult(reply, _default_scores(), {"mode": "stock"})
async def _synthesize_answer(
self,
question: str,
subanswers: list[str],
context: str,
classify: dict[str, Any],
plan: ModePlan,
call_llm: Callable[..., Any],
) -> str:
async def _synthesize_answer(self, question: str, subanswers: list[str], context: str, classify: dict[str, Any], plan: ModePlan, call_llm: Callable[..., Any]) -> str:
style_hint = _style_hint(classify)
if not subanswers:
prompt = (
@ -148,13 +125,7 @@ class AnswerEngine:
return drafts[idx]
return drafts[0]
async def _score_answer(
self,
question: str,
reply: str,
plan: ModePlan,
call_llm: Callable[..., Any],
) -> AnswerScores:
async def _score_answer(self, question: str, reply: str, plan: ModePlan, call_llm: Callable[..., Any]) -> AnswerScores:
if not plan.use_scores:
return _default_scores()
prompt = prompts.SCORE_PROMPT + "\nQuestion: " + question + "\nAnswer: " + reply
@ -162,14 +133,7 @@ class AnswerEngine:
data = _parse_json_block(raw, fallback={})
return _scores_from_json(data)
async def _extract_claims(
self,
question: str,
reply: str,
summary: dict[str, Any],
facts_used: list[str],
call_llm: Callable[..., Any],
) -> list[ClaimItem]:
async def _extract_claims(self, question: str, reply: str, summary: dict[str, Any], facts_used: list[str], call_llm: Callable[..., Any]) -> list[ClaimItem]:
if not reply or not summary:
return []
summary_json = _json_excerpt(summary)
@ -208,27 +172,13 @@ class AnswerEngine:
claims.append(ClaimItem(id=claim_id, claim=claim_text, evidence=evidence_items))
return claims
async def _dedup_reply(
self,
reply: str,
plan: ModePlan,
call_llm: Callable[..., Any],
tag: str,
) -> str:
async def _dedup_reply(self, reply: str, plan: ModePlan, call_llm: Callable[..., Any], tag: str) -> str:
if not _needs_dedup(reply):
return reply
dedup_prompt = prompts.DEDUP_PROMPT + "\nDraft: " + reply
return await call_llm(prompts.DEDUP_SYSTEM, dedup_prompt, model=plan.fast_model, tag=tag)
async def _answer_followup( # noqa: C901
self,
question: str,
state: ConversationState,
summary: dict[str, Any],
classify: dict[str, Any], # noqa: ARG002
plan: ModePlan,
call_llm: Callable[..., Any],
) -> str:
async def _answer_followup(self, question: str, state: ConversationState, summary: dict[str, Any], classify: dict[str, Any], plan: ModePlan, call_llm: Callable[..., Any]) -> str: # noqa: C901, ARG002
claim_ids = await self._select_claims(question, state.claims, plan, call_llm)
selected = [claim for claim in state.claims if claim.id in claim_ids] if claim_ids else state.claims[:2]
evidence_lines = []
@ -284,13 +234,7 @@ class AnswerEngine:
reply = _strip_followup_meta(reply)
return reply
async def _select_claims(
self,
question: str,
claims: list[ClaimItem],
plan: ModePlan,
call_llm: Callable[..., Any],
) -> list[str]:
async def _select_claims(self, question: str, claims: list[ClaimItem], plan: ModePlan, call_llm: Callable[..., Any]) -> list[str]:
if not claims:
return []
claims_brief = [{"id": claim.id, "claim": claim.claim} for claim in claims]
@ -308,14 +252,7 @@ class AnswerEngine:
state_payload = self._store.get(conversation_id)
return _state_from_payload(state_payload) if state_payload else None
def _store_state(
self,
conversation_id: str,
claims: list[ClaimItem],
summary: dict[str, Any],
snapshot: dict[str, Any] | None,
pin_snapshot: bool,
) -> None:
def _store_state(self, conversation_id: str, claims: list[ClaimItem], summary: dict[str, Any], snapshot: dict[str, Any] | None, pin_snapshot: bool) -> None:
snapshot_id = _snapshot_id(summary)
pinned_snapshot = snapshot if pin_snapshot else None
payload = {

View File

@ -76,13 +76,7 @@ def _is_plain_math_question(question: str) -> bool:
)
def _quick_fact_sheet_lines( # noqa: C901
question: str,
summary_lines: list[str],
kb_lines: list[str],
*,
limit: int,
) -> list[str]:
def _quick_fact_sheet_lines(question: str, summary_lines: list[str], kb_lines: list[str], *, limit: int) -> list[str]: # noqa: C901
tokens = {
token
for token in re.findall(r"[a-z0-9][a-z0-9_-]{2,}", question.lower())

View File

@ -59,10 +59,7 @@ def _needs_evidence_guard(reply: str, facts: list[str]) -> bool:
return any(term in lower_reply for term in arch_terms) and not any(term in fact_text for term in arch_terms)
async def _contradiction_decision(
ctx: ContradictionContext,
attempts: int = 1,
) -> dict[str, Any]:
async def _contradiction_decision(ctx: ContradictionContext, attempts: int = 1) -> dict[str, Any]:
best = {"use_facts": True, "confidence": 50}
facts_block = "\n".join(ctx.facts[:12])
for idx in range(max(1, attempts)):
@ -256,12 +253,7 @@ def _expand_tokens(tokens: list[str]) -> list[str]:
return expanded
def _ensure_token_coverage(
lines: list[str],
tokens: list[str],
summary_lines: list[str],
max_add: int = 4,
) -> list[str]:
def _ensure_token_coverage(lines: list[str], tokens: list[str], summary_lines: list[str], max_add: int = 4) -> list[str]:
if not lines or not tokens or not summary_lines:
return lines
hay = " ".join(lines).lower()

View File

@ -74,12 +74,7 @@ def _needs_focus_fix(question: str, reply: str, classify: dict[str, Any]) -> boo
return any(marker in reply.lower() for marker in extra_markers)
def _extract_keywords(
raw_question: str,
normalized: str,
sub_questions: list[str],
keywords: list[Any] | None,
) -> list[str]:
def _extract_keywords(raw_question: str, normalized: str, sub_questions: list[str], keywords: list[Any] | None) -> list[str]:
stopwords = {
"the",
"and",
@ -211,13 +206,10 @@ def _resolve_path(data: Any, path: str) -> Any | None:
else:
return None
if index is not None:
try:
idx = int(index)
if isinstance(cursor, list) and 0 <= idx < len(cursor):
cursor = cursor[idx]
else:
return None
except ValueError:
idx = int(index)
if isinstance(cursor, list) and 0 <= idx < len(cursor):
cursor = cursor[idx]
else:
return None
return cursor

View File

@ -125,9 +125,9 @@ def _metric_ctx_values(ctx: dict[str, Any]) -> tuple[list[str], str, list[str],
if not isinstance(summary_lines, list):
return [], "", [], [], set()
question = ctx.get("question") if isinstance(ctx, dict) else ""
sub_questions = ctx.get("sub_questions") if isinstance(ctx, dict) else []
keywords = ctx.get("keywords") if isinstance(ctx, dict) else []
keyword_tokens = ctx.get("keyword_tokens") if isinstance(ctx, dict) else []
sub_questions = ctx.get("sub_questions") if isinstance(ctx.get("sub_questions"), list) else []
keywords = ctx.get("keywords") if isinstance(ctx.get("keywords"), list) else []
keyword_tokens = ctx.get("keyword_tokens") if isinstance(ctx.get("keyword_tokens"), list) else []
token_set = {str(token).lower() for token in keyword_tokens if token}
token_set |= {token.lower() for token in _extract_keywords(str(question), str(question), sub_questions=sub_questions, keywords=keywords)}
token_set = _token_variants(token_set)

View File

@ -32,13 +32,7 @@ def _metric_key_tokens(summary_lines: list[str]) -> set[str]:
return tokens
async def _select_best_candidate(
call_llm: Callable[..., Any],
question: str,
candidates: list[str],
plan: ModePlan,
tag: str,
) -> int:
async def _select_best_candidate(call_llm: Callable[..., Any], question: str, candidates: list[str], plan: ModePlan, tag: str) -> int:
if len(candidates) <= 1:
return 0
prompt = (
@ -82,13 +76,7 @@ def _collect_fact_candidates(selected: list[dict[str, Any]], limit: int) -> list
return _dedupe_lines(lines, limit=limit)
async def _select_best_list(
call_llm: Callable[..., Any],
question: str,
candidates: list[list[str]],
plan: ModePlan,
tag: str,
) -> list[str]:
async def _select_best_list(call_llm: Callable[..., Any], question: str, candidates: list[list[str]], plan: ModePlan, tag: str) -> list[str]:
if not candidates:
return []
if len(candidates) == 1:
@ -106,12 +94,7 @@ async def _select_best_list(
return chosen
async def _extract_fact_types(
call_llm: Callable[..., Any],
question: str,
keywords: list[str],
plan: ModePlan,
) -> list[str]:
async def _extract_fact_types(call_llm: Callable[..., Any], question: str, keywords: list[str], plan: ModePlan) -> list[str]:
prompt = prompts.FACT_TYPES_PROMPT + "\nQuestion: " + question
if keywords:
prompt += "\nKeywords: " + ", ".join(keywords)
@ -130,12 +113,7 @@ async def _extract_fact_types(
return chosen[:10]
async def _derive_signals(
call_llm: Callable[..., Any],
question: str,
fact_types: list[str],
plan: ModePlan,
) -> list[str]:
async def _derive_signals(call_llm: Callable[..., Any], question: str, fact_types: list[str], plan: ModePlan) -> list[str]:
if not fact_types:
return []
prompt = prompts.SIGNAL_PROMPT.format(question=question, fact_types="; ".join(fact_types))
@ -154,13 +132,7 @@ async def _derive_signals(
return chosen[:12]
async def _scan_chunk_for_signals(
call_llm: Callable[..., Any],
question: str,
signals: list[str],
chunk_lines: list[str],
plan: ModePlan,
) -> list[str]:
async def _scan_chunk_for_signals(call_llm: Callable[..., Any], question: str, signals: list[str], chunk_lines: list[str], plan: ModePlan) -> list[str]:
if not signals or not chunk_lines:
return []
prompt = prompts.CHUNK_SCAN_PROMPT.format(
@ -183,13 +155,7 @@ async def _scan_chunk_for_signals(
return chosen[:15]
async def _prune_metric_candidates(
call_llm: Callable[..., Any],
question: str,
candidates: list[str],
plan: ModePlan,
attempts: int,
) -> list[str]:
async def _prune_metric_candidates(call_llm: Callable[..., Any], question: str, candidates: list[str], plan: ModePlan, attempts: int) -> list[str]:
if not candidates:
return []
prompt = prompts.FACT_PRUNE_PROMPT.format(question=question, candidates="\n".join(candidates), max_lines=6)
@ -208,13 +174,7 @@ async def _prune_metric_candidates(
return chosen[:6]
async def _select_fact_lines(
call_llm: Callable[..., Any],
question: str,
candidates: list[str],
plan: ModePlan,
max_lines: int,
) -> list[str]:
async def _select_fact_lines(call_llm: Callable[..., Any], question: str, candidates: list[str], plan: ModePlan, max_lines: int) -> list[str]:
if not candidates:
return []
prompt = prompts.FACT_PRUNE_PROMPT.format(question=question, candidates="\n".join(candidates), max_lines=max_lines)

View File

@ -23,16 +23,7 @@ from .retrieval_ext import *
from .spine import *
from .workflow_post import finalize_answer
async def run_answer( # noqa: C901
engine: Any,
question: str,
*,
mode: str,
history: list[dict[str, str]] | None = None,
observer: Callable[[str, str], None] | None = None,
conversation_id: str | None = None,
snapshot_pin: bool | None = None,
) -> AnswerResult:
async def run_answer(engine: Any, question: str, *, mode: str, history: list[dict[str, str]] | None = None, observer: Callable[[str, str], None] | None = None, conversation_id: str | None = None, snapshot_pin: bool | None = None) -> AnswerResult: # noqa: C901
"""Answer a question using the staged reasoning pipeline."""
settings = engine._settings
@ -69,14 +60,7 @@ async def run_answer( # noqa: C901
"evidence_fix",
}
async def call_llm(
system: str,
prompt: str,
*,
context: str | None = None,
model: str | None = None,
tag: str = "",
) -> str:
async def call_llm(system: str, prompt: str, *, context: str | None = None, model: str | None = None, tag: str = "") -> str:
nonlocal call_count, limit_hit, time_budget_hit
if not limitless and call_count >= call_cap:
limit_hit = True

View File

@ -15,32 +15,7 @@ from .retrieval import *
from .spine import *
async def finalize_answer( # noqa: C901
*,
engine: Any,
call_llm: Callable[..., Any],
normalized: str,
subanswers: list[str],
context: str,
classify: dict[str, Any],
plan: ModePlan,
summary: dict[str, Any],
summary_lines: list[str],
metric_facts: list[str],
key_facts: list[str],
facts_used: list[str],
allowed_nodes: list[str],
allowed_namespaces: list[str],
runbook_paths: list[str],
lowered_question: str,
force_metric: bool,
keyword_tokens: list[str],
question_tokens: list[str],
snapshot_context: str,
observer: Callable[[str, str], None] | None,
mode: str,
metric_keys: list[str] | None = None,
) -> tuple[str, AnswerScores, list[ClaimItem]]:
async def finalize_answer(*, engine: Any, call_llm: Callable[..., Any], normalized: str, subanswers: list[str], context: str, classify: dict[str, Any], plan: ModePlan, summary: dict[str, Any], summary_lines: list[str], metric_facts: list[str], key_facts: list[str], facts_used: list[str], allowed_nodes: list[str], allowed_namespaces: list[str], runbook_paths: list[str], lowered_question: str, force_metric: bool, keyword_tokens: list[str], question_tokens: list[str], snapshot_context: str, observer: Callable[[str, str], None] | None, mode: str, metric_keys: list[str] | None = None) -> tuple[str, AnswerScores, list[ClaimItem]]: # noqa: C901
"""Synthesize and post-process the final answer."""
reply = await engine._synthesize_answer(normalized, subanswers, context, classify, plan, call_llm)

View File

@ -49,14 +49,7 @@ async def main() -> None:
queue = QueueManager(settings, handler)
await queue.start()
async def answer_handler(
question: str,
mode: str,
history=None,
conversation_id=None,
snapshot_pin: bool | None = None,
observer=None,
) -> AnswerResult:
async def answer_handler(question: str, mode: str, history=None, conversation_id=None, snapshot_pin: bool | None = None, observer=None) -> AnswerResult:
if settings.queue_enabled:
payload = await queue.submit(
{

View File

@ -89,17 +89,7 @@ class MatrixClient:
class MatrixBot:
"""Drive Matrix conversation handling and heartbeat replies."""
def __init__(
self,
settings: Settings,
bot: MatrixBotConfig,
engine: AnswerEngine,
answer_handler: Callable[
[str, str, list[dict[str, str]] | None, str | None, Callable[[str, str], None] | None],
Awaitable[AnswerResult],
]
| None = None,
) -> None:
def __init__(self, settings: Settings, bot: MatrixBotConfig, engine: AnswerEngine, answer_handler: Callable[[str, str, list[dict[str, str]] | None, str | None, Callable[[str, str], None] | None], Awaitable[AnswerResult]] | None = None) -> None:
self._settings = settings
self._bot = bot
self._engine = engine

View File

@ -150,11 +150,7 @@ def _merge_cluster_summary(snapshot: dict[str, Any], summary: dict[str, Any]) ->
)
def _merge_cluster_fields(
summary: dict[str, Any],
cluster_summary: dict[str, Any],
field_types: dict[str, type],
) -> None:
def _merge_cluster_fields(summary: dict[str, Any], cluster_summary: dict[str, Any], field_types: dict[str, type]) -> None:
for key, expected in field_types.items():
value = cluster_summary.get(key)
if isinstance(value, expected):
@ -249,10 +245,7 @@ def _build_hardware_by_node(nodes_detail: list[dict[str, Any]]) -> dict[str, Any
return {"hardware_by_node": mapping} if mapping else {}
def _build_hardware_usage( # noqa: C901
metrics: dict[str, Any],
hardware_by_node: dict[str, Any] | None,
) -> dict[str, Any]:
def _build_hardware_usage(metrics: dict[str, Any], hardware_by_node: dict[str, Any] | None) -> dict[str, Any]: # noqa: C901
if not isinstance(hardware_by_node, dict) or not hardware_by_node:
return {}
node_load = metrics.get("node_load") if isinstance(metrics.get("node_load"), list) else []

View File

@ -470,7 +470,9 @@ def _append_pvc_usage(lines: list[str], summary: dict[str, Any]) -> None:
return
parts = []
for entry in pvc_usage:
metric = entry.get("metric") if isinstance(entry, dict) else {}
if not isinstance(entry, dict):
continue
metric = entry.get("metric") if isinstance(entry.get("metric"), dict) else {}
namespace = metric.get("namespace")
pvc = metric.get("persistentvolumeclaim")
value = entry.get("value")
@ -478,8 +480,6 @@ def _append_pvc_usage(lines: list[str], summary: dict[str, Any]) -> None:
parts.append(f"{namespace}/{pvc}={_format_float(value)}%")
if parts:
lines.append("pvc_usage_top: " + "; ".join(parts))
def _append_root_disk_headroom(lines: list[str], summary: dict[str, Any]) -> None:
headroom = summary.get("root_disk_low_headroom")
if not isinstance(headroom, list) or not headroom:

View File

@ -6,6 +6,25 @@ from .core_a import _VALUE_PAIR_LEN
from .format_a import *
def _append_namespace_metric_series(
lines: list[str],
label: str,
entries: list[Any],
formatter: Any,
) -> None:
parts = []
for entry in entries:
if not isinstance(entry, dict):
continue
metric = entry.get("metric") if isinstance(entry.get("metric"), dict) else {}
namespace = metric.get("namespace")
value = entry.get("value")
if namespace:
parts.append(f"{namespace}={formatter(value)}")
if parts:
lines.append(f"{label}: " + "; ".join(parts))
def _append_longhorn(lines: list[str], summary: dict[str, Any]) -> None: # noqa: C901
longhorn = summary.get("longhorn") if isinstance(summary.get("longhorn"), dict) else {}
if not longhorn:
@ -52,78 +71,24 @@ def _append_namespace_usage(lines: list[str], summary: dict[str, Any]) -> None:
metrics = summary.get("metrics") if isinstance(summary.get("metrics"), dict) else {}
cpu_top = metrics.get("namespace_cpu_top") if isinstance(metrics.get("namespace_cpu_top"), list) else []
mem_top = metrics.get("namespace_mem_top") if isinstance(metrics.get("namespace_mem_top"), list) else []
if cpu_top:
parts = []
for entry in cpu_top:
metric = entry.get("metric") if isinstance(entry, dict) else {}
namespace = metric.get("namespace")
value = entry.get("value")
if namespace:
parts.append(f"{namespace}={_format_float(value)}")
if parts:
lines.append("namespace_cpu_top: " + "; ".join(parts))
if mem_top:
parts = []
for entry in mem_top:
metric = entry.get("metric") if isinstance(entry, dict) else {}
namespace = metric.get("namespace")
value = entry.get("value")
if namespace:
parts.append(f"{namespace}={_format_bytes(value)}")
if parts:
lines.append("namespace_mem_top: " + "; ".join(parts))
_append_namespace_metric_series(lines, "namespace_cpu_top", cpu_top, _format_float)
_append_namespace_metric_series(lines, "namespace_mem_top", mem_top, _format_bytes)
def _append_namespace_requests(lines: list[str], summary: dict[str, Any]) -> None:
metrics = summary.get("metrics") if isinstance(summary.get("metrics"), dict) else {}
cpu_req = metrics.get("namespace_cpu_requests_top") if isinstance(metrics.get("namespace_cpu_requests_top"), list) else []
mem_req = metrics.get("namespace_mem_requests_top") if isinstance(metrics.get("namespace_mem_requests_top"), list) else []
if cpu_req:
parts = []
for entry in cpu_req:
metric = entry.get("metric") if isinstance(entry, dict) else {}
namespace = metric.get("namespace")
value = entry.get("value")
if namespace:
parts.append(f"{namespace}={_format_float(value)}")
if parts:
lines.append("namespace_cpu_requests_top: " + "; ".join(parts))
if mem_req:
parts = []
for entry in mem_req:
metric = entry.get("metric") if isinstance(entry, dict) else {}
namespace = metric.get("namespace")
value = entry.get("value")
if namespace:
parts.append(f"{namespace}={_format_bytes(value)}")
if parts:
lines.append("namespace_mem_requests_top: " + "; ".join(parts))
_append_namespace_metric_series(lines, "namespace_cpu_requests_top", cpu_req, _format_float)
_append_namespace_metric_series(lines, "namespace_mem_requests_top", mem_req, _format_bytes)
def _append_namespace_io_net(lines: list[str], summary: dict[str, Any]) -> None:
metrics = summary.get("metrics") if isinstance(summary.get("metrics"), dict) else {}
net_top = metrics.get("namespace_net_top") if isinstance(metrics.get("namespace_net_top"), list) else []
io_top = metrics.get("namespace_io_top") if isinstance(metrics.get("namespace_io_top"), list) else []
if net_top:
parts = []
for entry in net_top:
metric = entry.get("metric") if isinstance(entry, dict) else {}
namespace = metric.get("namespace")
value = entry.get("value")
if namespace:
parts.append(f"{namespace}={_format_rate_bytes(value)}")
if parts:
lines.append("namespace_net_top: " + "; ".join(parts))
if io_top:
parts = []
for entry in io_top:
metric = entry.get("metric") if isinstance(entry, dict) else {}
namespace = metric.get("namespace")
value = entry.get("value")
if namespace:
parts.append(f"{namespace}={_format_rate_bytes(value)}")
if parts:
lines.append("namespace_io_top: " + "; ".join(parts))
_append_namespace_metric_series(lines, "namespace_net_top", net_top, _format_rate_bytes)
_append_namespace_metric_series(lines, "namespace_io_top", io_top, _format_rate_bytes)
def _append_pod_usage(lines: list[str], summary: dict[str, Any]) -> None: # noqa: C901
@ -143,7 +108,9 @@ def _append_pod_usage(lines: list[str], summary: dict[str, Any]) -> None: # noq
if cpu_top:
parts = []
for entry in cpu_top:
metric = entry.get("metric") if isinstance(entry, dict) else {}
if not isinstance(entry, dict):
continue
metric = entry.get("metric") if isinstance(entry.get("metric"), dict) else {}
namespace = metric.get("namespace")
pod = metric.get("pod")
value = entry.get("value")
@ -154,7 +121,9 @@ def _append_pod_usage(lines: list[str], summary: dict[str, Any]) -> None: # noq
if cpu_top_node:
parts = []
for entry in cpu_top_node:
metric = entry.get("metric") if isinstance(entry, dict) else {}
if not isinstance(entry, dict):
continue
metric = entry.get("metric") if isinstance(entry.get("metric"), dict) else {}
namespace = metric.get("namespace")
pod = metric.get("pod")
node = metric.get("node")
@ -166,7 +135,9 @@ def _append_pod_usage(lines: list[str], summary: dict[str, Any]) -> None: # noq
if mem_top:
parts = []
for entry in mem_top:
metric = entry.get("metric") if isinstance(entry, dict) else {}
if not isinstance(entry, dict):
continue
metric = entry.get("metric") if isinstance(entry.get("metric"), dict) else {}
namespace = metric.get("namespace")
pod = metric.get("pod")
value = entry.get("value")
@ -177,7 +148,9 @@ def _append_pod_usage(lines: list[str], summary: dict[str, Any]) -> None: # noq
if mem_top_node:
parts = []
for entry in mem_top_node:
metric = entry.get("metric") if isinstance(entry, dict) else {}
if not isinstance(entry, dict):
continue
metric = entry.get("metric") if isinstance(entry.get("metric"), dict) else {}
namespace = metric.get("namespace")
pod = metric.get("pod")
node = metric.get("node")
@ -230,7 +203,9 @@ def _append_job_failures(lines: list[str], summary: dict[str, Any]) -> None:
return
parts = []
for entry in failures:
metric = entry.get("metric") if isinstance(entry, dict) else {}
if not isinstance(entry, dict):
continue
metric = entry.get("metric") if isinstance(entry.get("metric"), dict) else {}
namespace = metric.get("namespace")
job_name = metric.get("job_name") or metric.get("job")
value = entry.get("value")
@ -323,7 +298,9 @@ def _append_postgres(lines: list[str], summary: dict[str, Any]) -> None:
if isinstance(by_db, list) and by_db:
parts = []
for entry in by_db:
metric = entry.get("metric") if isinstance(entry, dict) else {}
if not isinstance(entry, dict):
continue
metric = entry.get("metric") if isinstance(entry.get("metric"), dict) else {}
value = entry.get("value")
if isinstance(value, list) and len(value) >= _VALUE_PAIR_LEN:
value = value[1]

View File

@ -2,6 +2,7 @@ from __future__ import annotations
from typing import Any
from .core_a import PVC_USAGE_CRITICAL
from .format_b import *
def _append_signals(lines: list[str], summary: dict[str, Any]) -> None:
signals = summary.get("signals") if isinstance(summary.get("signals"), list) else []

View File

@ -5,35 +5,20 @@ from __future__ import annotations
import argparse
import json
from datetime import date
from pathlib import Path
def main() -> int: # noqa: C901
def main() -> int:
"""Check each production file against a minimum coverage percentage."""
parser = argparse.ArgumentParser()
parser.add_argument("coverage_json")
parser.add_argument("--root", default="atlasbot")
parser.add_argument("--threshold", type=float, default=95.0)
parser.add_argument("--exceptions-file", default="testing/coverage_exceptions.json")
args = parser.parse_args()
data = json.loads(Path(args.coverage_json).read_text(encoding="utf-8"))
files = data.get("files") if isinstance(data, dict) else {}
exceptions_path = Path(args.exceptions_file)
per_file_thresholds: dict[str, float] = {}
if exceptions_path.exists():
payload = json.loads(exceptions_path.read_text(encoding="utf-8"))
expires_on = str(payload.get("expires_on") or "").strip()
if expires_on and date.today() > date.fromisoformat(expires_on):
print(f"coverage exceptions expired on {expires_on}: {exceptions_path}")
return 1
overrides = payload.get("per_file_thresholds")
if isinstance(overrides, dict):
for path, threshold in overrides.items():
if isinstance(path, str) and isinstance(threshold, (int, float)):
per_file_thresholds[path] = float(threshold)
violations: list[tuple[float, str]] = []
for path, payload in sorted(files.items()):
if not path.startswith(f"{args.root}/"):
@ -42,9 +27,8 @@ def main() -> int: # noqa: C901
percent = summary.get("percent_covered") if isinstance(summary, dict) else None
if not isinstance(percent, (int, float)):
continue
threshold = per_file_thresholds.get(path, float(args.threshold))
if float(percent) < threshold:
violations.append((float(percent), f"{path} (min {threshold:.2f}%)"))
if float(percent) < args.threshold:
violations.append((float(percent), path))
if violations:
for percent, path in sorted(violations):
@ -55,3 +39,4 @@ def main() -> int: # noqa: C901
if __name__ == "__main__":
raise SystemExit(main())

File diff suppressed because it is too large Load Diff