Coverage for src/local_deep_research/news/utils/headline_generator.py: 100%
29 statements
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-20 01:24 +0000
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-20 01:24 +0000
1"""
2Headline generation utilities for news items.
3Uses LLM to generate concise, meaningful headlines from long queries and findings.
4"""
6from typing import Optional
7from loguru import logger
10def generate_headline(
11 query: str,
12 findings: str = "",
13 max_length: int = 100,
14 settings_snapshot: Optional[dict] = None,
15) -> str:
16 """
17 Generate a concise headline from a query and optional findings.
19 Args:
20 query: The search query or research question
21 findings: Optional findings/content to help generate better headline
22 max_length: Maximum length for the headline
23 settings_snapshot: Optional settings snapshot so the LLM call
24 picks up the active egress policy. Background callers
25 should pass this through; without it, the LLM PEP's
26 ``settings_snapshot is not None`` guard skips and a cloud
27 LLM can fire even under require_local_endpoint.
29 Returns:
30 A concise headline string
31 """
32 # Always try LLM generation first for dynamic headlines based on actual content
33 llm_headline = _generate_with_llm(
34 query, findings, max_length, settings_snapshot
35 )
36 if llm_headline:
37 return llm_headline
39 # No fallback - if LLM fails, indicate failure
40 return "[Headline generation failed]"
43def _generate_with_llm(
44 query: str,
45 findings: str,
46 max_length: int,
47 settings_snapshot: Optional[dict] = None,
48) -> Optional[str]:
49 """Generate headline using LLM."""
50 try:
51 from ...config.llm_config import get_llm
53 # Use the configured model for headline generation
54 llm = get_llm(temperature=0.3, settings_snapshot=settings_snapshot)
56 try:
57 # Focus only on the findings/report content, not the query
58 if not findings:
59 logger.debug("No findings provided for headline generation")
60 return None
62 # Use the COMPLETE findings - no character limit
63 findings_preview = findings
64 logger.debug(
65 f"Generating headline with {len(findings)} chars of findings"
66 )
68 prompt = f"""Generate a comprehensive news headline that captures the key events from the research report below.
70Research Findings:
71{findings_preview}
73Requirements:
74- Include MULTIPLE major events if several important things happened (e.g., "Earthquake Strikes California While Wildfires Rage; Global Markets Tumble Amid Political Tensions")
75- Capture as much important information as possible in the headline
76- Be specific about locations, impacts, and key details
77- Professional news headline style but can be longer to include more information
78- Focus on the most impactful findings from the report
79- Use semicolons or commas to separate multiple major events
80- No quotes or punctuation at start/end
81- Base the headline ONLY on the actual findings in the report
83Generate only the headline text, nothing else."""
85 response = llm.invoke(prompt)
86 headline: str = str(response.content).strip()
88 # Clean up the generated headline
89 headline = headline.strip("\"'.,!?")
91 # Validate the headline
92 if headline:
93 logger.debug(f"Generated headline: {headline}")
94 return headline
95 finally:
96 from ...utilities.resource_utils import safe_close
98 safe_close(llm, "headline LLM")
100 except Exception as e:
101 logger.debug(f"LLM headline generation failed: {e}")
103 return None