Coverage for src/local_deep_research/advanced_search_system/summarization/base.py: 100%
27 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-03 23:15 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-03 23:15 +0000
1"""Abstract base for LLM-driven summarizers."""
3from abc import ABC, abstractmethod
5from langchain_core.language_models.chat_models import BaseChatModel
6from loguru import logger
8from ...utilities.search_utilities import remove_think_tags
11class BaseSummarizer(ABC):
12 """Common machinery for invoking an LLM to summarize text.
14 Subclasses provide the prompt via :meth:`_build_prompt`. The base class
15 handles the model invocation, think-tag stripping, and length truncation.
17 On LLM-invocation failure (network error, rate limit, malformed response)
18 :meth:`summarize` returns an empty string. This is a deliberate choice for
19 callers that aggregate the summary into a larger context update — losing
20 one summary turn should not abort the rest of the update.
21 """
23 INPUT_TRUNCATE_CHARS = 8000
25 def __init__(
26 self,
27 model: BaseChatModel,
28 max_sentences: int = 3,
29 max_chars: int = 300,
30 ):
31 self.model = model
32 self.max_sentences = max_sentences
33 self.max_chars = max_chars
35 def summarize(self, content: str) -> str:
36 if not content:
37 return ""
39 prompt = self._build_prompt(content[: self.INPUT_TRUNCATE_CHARS])
41 try:
42 response = self.model.invoke(prompt)
43 except Exception:
44 logger.opt(exception=True).debug("LLM summarization failed")
45 return ""
47 summary = remove_think_tags(str(response.content)).strip()
48 return self._truncate(summary)
50 @abstractmethod
51 def _build_prompt(self, content: str) -> str:
52 """Return the prompt sent to the LLM for ``content``."""
54 def _truncate(self, summary: str) -> str:
55 if len(summary) > self.max_chars:
56 return summary[: self.max_chars].rstrip() + "..."
57 return summary