diff --git a/goodnews/llm.py b/goodnews/llm.py index ae8a6a4..0f542a6 100644 --- a/goodnews/llm.py +++ b/goodnews/llm.py @@ -206,7 +206,15 @@ class LocalModelClient: names.append(str(model["id"])) return names - def _chat(self, payload: dict) -> dict: + def chat_text(self, messages: list[dict]) -> str: + """Plain chat completion → the raw message text (no JSON parsing). + + Used for free-form output like summaries; classification uses _chat, + which JSON-parses the same content. + """ + return self._raw_content(self._build_payload(messages, None)) + + def _raw_content(self, payload: dict) -> str: body = json.dumps(payload).encode("utf-8") headers = {"Content-Type": "application/json"} if self.api_key: @@ -227,10 +235,12 @@ class LocalModelClient: raise RuntimeError(f"could not reach local model at {self.base_url}: {exc.reason}") from exc try: - content = data["choices"][0]["message"]["content"] + return data["choices"][0]["message"]["content"] except (KeyError, IndexError, TypeError) as exc: raise RuntimeError(f"unexpected local model response: {data}") from exc - return parse_classifier_json(content) + + def _chat(self, payload: dict) -> dict: + return parse_classifier_json(self._raw_content(payload)) @dataclass diff --git a/goodnews/summarize.py b/goodnews/summarize.py index 01d6b43..6d40ca4 100644 --- a/goodnews/summarize.py +++ b/goodnews/summarize.py @@ -72,9 +72,7 @@ def summarize_article(client: LocalModelClient, title: str, snippet: str, body_t material = (body_text or snippet or title or "")[:4000] user = f"Title: {title}\n\nArticle text:\n{material}\n\nWrite the summary." messages = [{"role": "system", "content": _SYSTEM}, {"role": "user", "content": user}] - resp = client._chat(client._build_payload(messages, None)) - content = (resp.get("choices") or [{}])[0].get("message", {}).get("content") or "" - return content.strip()[:1200] + return (client.chat_text(messages) or "").strip()[:1200] def get_summary(conn: sqlite3.Connection, article_id: int) -> str | None: