feat(diagnose): 5-stage multi-agent diagnose pipeline (#29) #39

Merged
pyr0ball merged 17 commits from feat/29-multi-agent-diagnose into main 2026-05-25 19:59:35 -07:00
2 changed files with 45 additions and 2 deletions
Showing only changes of commit e8c66972fa - Show all commits

View file

@ -30,6 +30,14 @@ _SYSTEM_PROMPT = (
) )
def _coerce_float(val: object, default: float) -> float:
"""Safely coerce LLM output to float, returning default on failure."""
try:
return float(val) # type: ignore[arg-type]
except (TypeError, ValueError):
return default
def _validate_severity(s: str) -> SeverityLabel: def _validate_severity(s: str) -> SeverityLabel:
"""Map a raw severity string to a valid SeverityLabel, defaulting to ERROR.""" """Map a raw severity string to a valid SeverityLabel, defaulting to ERROR."""
upper = s.upper() upper = s.upper()
@ -198,8 +206,8 @@ class RootCauseHypothesizer:
hypothesis_id=str(uuid4()), hypothesis_id=str(uuid4()),
title=str(item.get("title", "Unknown"))[:80], title=str(item.get("title", "Unknown"))[:80],
description=str(item.get("description", "")), description=str(item.get("description", "")),
confidence=float(item.get("confidence", 0.5)), confidence=_coerce_float(item.get("confidence"), 0.5),
supporting_cluster_ids=tuple(item.get("supporting_clusters", [])), supporting_cluster_ids=tuple(item.get("supporting_clusters") or []),
runbook_refs=(), runbook_refs=(),
severity=severity, severity=severity,
) )

View file

@ -449,3 +449,38 @@ def test_confidence_string_float_coercion():
assert len(results) == 1 assert len(results) == 1
assert isinstance(results[0].confidence, float) assert isinstance(results[0].confidence, float)
assert results[0].confidence == pytest.approx(0.8) assert results[0].confidence == pytest.approx(0.8)
# ---------------------------------------------------------------------------
# Test 10: Non-numeric confidence string falls back to default 0.5
# ---------------------------------------------------------------------------
def test_non_numeric_confidence_uses_default():
"""LLM returning 'high' for confidence should not raise and defaults to 0.5."""
cluster = _make_cluster()
classified = _make_classified(clusters=(cluster,))
ctx = _make_ctx()
hypothesizer = RootCauseHypothesizer()
items = [
{
"title": "t",
"description": "d",
"confidence": "high",
"severity": "ERROR",
"supporting_clusters": [],
}
]
mock_resp = _llm_json_response(items)
with patch("httpx.post", return_value=mock_resp):
results = hypothesizer.hypothesize(
classified, ctx, query="test",
llm_url="http://localhost:11434",
llm_model="llama3",
)
assert len(results) == 1
assert isinstance(results[0].confidence, float)
assert results[0].confidence == pytest.approx(0.5)