Skip to content

Commit 3e25023

Browse files
refactor: extract generate_summary helper so view gets a timeout and task keeps retries
1 parent 7d44244 commit 3e25023

2 files changed

Lines changed: 49 additions & 16 deletions

File tree

news/tasks.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,23 @@
1212
logger = structlog.get_logger(__name__)
1313

1414

15-
@app.task(bind=True, max_retries=3, autoretry_for=(OpenAIError,))
16-
def summarize_content(
17-
self, content: str, title: str, model: str, max_length: int = 256
18-
) -> str:
19-
"""Summarize content using an LLM model."""
15+
def generate_summary(
16+
content: str,
17+
title: str,
18+
model: str,
19+
max_length: int = 256,
20+
timeout: float = 30,
21+
) -> str | None:
22+
"""Build the summarization prompt and call OpenRouter synchronously.
23+
24+
Plain function (no Celery decoration) so callers can use it inline with an
25+
explicit ``timeout``.
26+
Background callers go through ``summarize_content`` below, which wraps this
27+
in a Celery task so ``autoretry_for=(OpenAIError,)`` and ``max_retries`` fire.
28+
29+
Raises ValueError on empty content, OpenAIError on API failures.
30+
Returns the summary string, or None if the response is malformed.
31+
"""
2032
if not content:
2133
logger.warning("No content provided to summarize, skipping.")
2234
raise ValueError("No content provided to summarize.")
@@ -55,17 +67,33 @@ def summarize_content(
5567
{"role": "user", "content": user_prompt},
5668
]
5769
logger.debug(f"{messages=}")
58-
content = None
70+
client = OpenAI(
71+
base_url=OPENROUTER_URL, api_key=OPENROUTER_API_KEY, timeout=timeout
72+
)
73+
response = client.chat.completions.create(model=model, messages=messages)
5974
try:
60-
client = OpenAI(base_url=OPENROUTER_URL, api_key=OPENROUTER_API_KEY)
61-
response = client.chat.completions.create(model=model, messages=messages)
62-
content = response.choices[0].message.content
63-
logger.info(
64-
f"Received summarized content for {content[:100]=}: {len(content)=}..."
65-
)
75+
summary = response.choices[0].message.content
6676
except (AttributeError, IndexError) as e:
6777
logger.error(f"Error getting summarized content: {e=}")
68-
return content
78+
return None
79+
logger.info(
80+
f"Received summarized content for {summary[:100]=}: {len(summary)=}..."
81+
)
82+
return summary
83+
84+
85+
@app.task(bind=True, max_retries=3, autoretry_for=(OpenAIError,))
86+
def summarize_content(
87+
self, content: str, title: str, model: str, max_length: int = 256
88+
) -> str | None:
89+
"""Celery wrapper around ``generate_summary``.
90+
91+
Runs in a worker so ``autoretry_for=(OpenAIError,)`` and ``max_retries``
92+
apply to transient OpenRouter blips. For synchronous, inline callers, use
93+
``generate_summary`` directly (so retries don't silently no-op and you can
94+
set a tight timeout).
95+
"""
96+
return generate_summary(content, title, model, max_length)
6997

7098

7199
@app.task

news/views.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
)
4747
from .forms import BlogPostForm, EntryForm, LinkForm, NewsForm, PollForm, VideoForm
4848
from .models import BlogPost, Entry, Link, News, Poll, Video
49-
from .tasks import summarize_content
49+
from .tasks import generate_summary
5050
from .notifications import (
5151
send_email_news_approved,
5252
send_email_news_needs_moderation,
@@ -598,8 +598,13 @@ def generate_description(request):
598598
return JsonResponse({"error": "Add some content first."}, status=400)
599599

600600
try:
601-
summary = summarize_content(
602-
content, title, settings.SUMMARIZATION_MODEL, DESCRIPTION_SUMMARY_MAX_LENGTH
601+
# Call the plain helper
602+
summary = generate_summary(
603+
content,
604+
title,
605+
settings.SUMMARIZATION_MODEL,
606+
DESCRIPTION_SUMMARY_MAX_LENGTH,
607+
timeout=30,
603608
)
604609
except Exception:
605610
logger.exception("generate_description: summarization failed")

0 commit comments

Comments
 (0)