turnstone/app/services/llm.py

56 lines
1.7 KiB
Python

import logging
import httpx
from app.services.search import SearchResult
logger = logging.getLogger(__name__)
_SEVERITY_RANK = {"CRITICAL": 0, "ERROR": 1, "WARN": 2, "WARNING": 2}
_PROMPT_TEMPLATE = """\
You are a homelab diagnostic assistant. A user described a symptom and the system retrieved relevant log entries.
Analyze the log entries below and write a 2-4 sentence plain-language diagnosis. Focus on errors and their likely root cause. Be specific and concise — name the services involved, not generic platitudes.
User query: {query}
Log entries ({n} shown, highest severity first):
{log_block}
Diagnosis:"""
def _build_context(entries: list[SearchResult], max_entries: int = 25) -> str:
ranked = sorted(
entries,
key=lambda e: (_SEVERITY_RANK.get(e.severity or "", 3), e.timestamp_iso or ""),
)[:max_entries]
return "\n".join(
f"[{e.timestamp_iso or '?'}] [{e.severity or 'INFO'}] {e.text[:200]}"
for e in ranked
)
def summarize(
query: str,
entries: list[SearchResult],
llm_url: str,
llm_model: str,
timeout: float = 20.0,
) -> str | None:
if not entries:
return None
log_block = _build_context(entries)
prompt = _PROMPT_TEMPLATE.format(query=query, n=min(len(entries), 25), log_block=log_block)
try:
resp = httpx.post(
f"{llm_url.rstrip('/')}/api/generate",
json={"model": llm_model, "prompt": prompt, "stream": False},
timeout=timeout,
)
resp.raise_for_status()
return resp.json().get("response", "").strip() or None
except Exception as exc:
logger.warning("LLM summarization failed (%s): %s", type(exc).__name__, exc)
return None