125 lines
5.1 KiB
Python
Executable file
125 lines
5.1 KiB
Python
Executable file
"""
|
|
Focus Group Summary Service
|
|
|
|
This service generates LLM-powered one-line summaries for focus groups,
|
|
designed for display in the focus group list view to help users quickly
|
|
understand what each session is about.
|
|
"""
|
|
|
|
from typing import Dict, Any, Optional
|
|
import logging
|
|
|
|
from app.services.llm_service import LLMService, LLMServiceError
|
|
from app.utils.prompt_loader import load_prompt, PromptLoaderError
|
|
|
|
# Set up logger for this module
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FocusGroupSummaryError(Exception):
|
|
"""Custom exception for focus group summary generation errors."""
|
|
pass
|
|
|
|
|
|
async def generate_focus_group_summary(
|
|
focus_group_data: Dict[str, Any],
|
|
temperature: float = 0.7,
|
|
llm_model: Optional[str] = None
|
|
) -> Optional[str]:
|
|
"""
|
|
Generate a one-line summary of a focus group for display in list views.
|
|
|
|
Args:
|
|
focus_group_data: Dictionary containing:
|
|
- name: Focus group name
|
|
- topic: Discussion topic
|
|
- duration: Session duration in minutes
|
|
- description: Research brief/objective
|
|
- discussionGuide: (optional) The generated discussion guide
|
|
temperature: LLM temperature setting (default 0.7 for balanced creativity)
|
|
llm_model: Optional LLM model override
|
|
|
|
Returns:
|
|
A concise one-line summary string (max ~100 characters), or None on failure
|
|
"""
|
|
try:
|
|
# Extract data from focus group
|
|
name = focus_group_data.get('name', 'Unnamed Focus Group')
|
|
topic = focus_group_data.get('topic', 'General Research')
|
|
duration = focus_group_data.get('duration', 60)
|
|
description = focus_group_data.get('description') or focus_group_data.get('objective', '')
|
|
discussion_guide = focus_group_data.get('discussionGuide', '')
|
|
|
|
# Build discussion guide section for prompt
|
|
discussion_guide_section = ""
|
|
if discussion_guide:
|
|
# Handle both string and dict formats for discussion guide
|
|
if isinstance(discussion_guide, dict):
|
|
guide_title = discussion_guide.get('title', '')
|
|
guide_sections = discussion_guide.get('sections', [])
|
|
guide_text = f"Discussion Guide Title: {guide_title}\n"
|
|
for section in guide_sections[:3]: # Limit to first 3 sections to avoid too much context
|
|
section_title = section.get('title', '')
|
|
section_content = section.get('content', '')[:200] # Truncate content
|
|
guide_text += f"- {section_title}: {section_content}...\n"
|
|
discussion_guide_section = f"Discussion Guide Overview:\n{guide_text}"
|
|
elif isinstance(discussion_guide, str) and len(discussion_guide) > 0:
|
|
# Truncate long guides
|
|
truncated_guide = discussion_guide[:500] + "..." if len(discussion_guide) > 500 else discussion_guide
|
|
discussion_guide_section = f"Discussion Guide Overview:\n{truncated_guide}"
|
|
|
|
# Load and format the prompt
|
|
try:
|
|
final_prompt = load_prompt('focus-group-summary-generation', {
|
|
'name': name,
|
|
'topic': topic,
|
|
'duration': str(duration),
|
|
'description': description or 'Not specified',
|
|
'discussion_guide_section': discussion_guide_section
|
|
})
|
|
except PromptLoaderError as e:
|
|
logger.error(f"Error loading focus group summary prompt: {e}")
|
|
raise FocusGroupSummaryError(f"Error loading summary prompt: {str(e)}")
|
|
|
|
# Log the LLM API call
|
|
logger.info(f"Generating summary for focus group: {name}")
|
|
print(f"🤖 Backend: Generating one-line summary for focus group '{name}' using {llm_model or 'default model'}")
|
|
|
|
try:
|
|
raw_response = await LLMService.generate_content(
|
|
prompt=final_prompt,
|
|
temperature=temperature,
|
|
model_name=llm_model
|
|
)
|
|
|
|
# Clean up the response
|
|
summary = raw_response.strip()
|
|
|
|
# Remove any quotes that might wrap the response
|
|
if summary.startswith('"') and summary.endswith('"'):
|
|
summary = summary[1:-1]
|
|
if summary.startswith("'") and summary.endswith("'"):
|
|
summary = summary[1:-1]
|
|
|
|
# Remove any markdown formatting
|
|
if summary.startswith("```"):
|
|
summary = summary.strip("```").strip()
|
|
|
|
# Truncate if too long (enforce max 150 characters for safety)
|
|
if len(summary) > 150:
|
|
summary = summary[:147] + "..."
|
|
|
|
logger.info(f"Generated summary for '{name}': {summary}")
|
|
print(f"✅ Generated summary: {summary}")
|
|
|
|
return summary
|
|
|
|
except LLMServiceError as e:
|
|
logger.error(f"LLM service error generating summary: {e}")
|
|
raise FocusGroupSummaryError(f"Error from LLM service: {str(e)}")
|
|
|
|
except FocusGroupSummaryError:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error generating focus group summary: {e}")
|
|
raise FocusGroupSummaryError(f"Error generating focus group summary: {str(e)}")
|