Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Response generation module for RAG system. | |
| Handles LLM-based catalog summary generation and JSON parsing. | |
| """ | |
| import re | |
| import json | |
| import time | |
| import logging | |
| from typing import Any, List | |
| from pydantic import ValidationError | |
| from langchain_core.documents import Document | |
| from .models import CatalogResponse | |
| def generate_catalog_summary( | |
| llm: Any, | |
| query: str, | |
| context: str | |
| ) -> str: | |
| """ | |
| Generate a catalog-style summary using LLM. | |
| Args: | |
| llm: Language model instance | |
| query: Original (or expanded) user query | |
| context: Context string from reranked documents | |
| Returns: | |
| Summary string describing available catalog materials | |
| """ | |
| logging.info("ποΈ Generating final LLM summary...") | |
| start = time.time() | |
| # Catalog-focused prompt | |
| prompt = f"""You are a professional librarian at the Boston Public Library helping a patron find relevant materials. | |
| IMPORTANT: You only have access to CATALOG METADATA (titles, dates, locations, subjects, collections) - NOT the actual content of documents. | |
| Your task: Based on the catalog entries below, tell the patron which items, collections, or materials might be relevant to their query. | |
| Guidelines: | |
| - List the most relevant items found (titles, dates, collections) | |
| - Mention key time periods, locations, or subjects that appear | |
| - If results include newspapers, mention specific editions and dates | |
| - If results include images/photographs, describe what collections they're from | |
| - Be helpful even if results aren't perfect - describe what WAS found | |
| - If very few relevant items, suggest broader search terms | |
| - DO NOT make up information not in the catalog entries | |
| - DO NOT try to answer factual questions - only describe available materials | |
| Respond ONLY in valid JSON format: | |
| {{ | |
| "summary": "Your response describing what catalog items are available" | |
| }} | |
| Example response format: | |
| "I found several relevant items in our collection: The Dorchester Beacon newspaper has multiple editions from 1919 that likely covered major Boston events, including editions from January 11, 1919 (around the time of the molasses disaster) and several from September-November 1919 (Boston Police Strike period). These are available in the Boston Public Library Newspapers collection. I also found references to North End historical materials from this era." | |
| Catalog Entries: | |
| {context} | |
| Patron's Query: {query} | |
| """ | |
| logging.info("π LLM prompt for summary:\n%s", prompt[:1500]) | |
| response = llm.invoke(prompt) | |
| logging.info("π© LLM raw response (summary): %s", response.content[:2000]) | |
| parsed = parse_json_response(response.content) | |
| logging.info(f"β Summary generated in {time.time() - start:.2f}s.") | |
| return parsed | |
| def parse_json_response(output: str) -> str: | |
| """ | |
| Parse JSON response safely and return the summary. | |
| Automatically strips markdown code fences if present. | |
| Args: | |
| output: Raw LLM output string | |
| Returns: | |
| Parsed summary string, or raw output if parsing fails | |
| """ | |
| try: | |
| # Clean and normalize the model output | |
| output = output.strip() | |
| if output.startswith("```"): | |
| output = re.sub(r"^```[a-zA-Z0-9]*\n?", "", output) | |
| output = re.sub(r"```$", "", output) | |
| output = output.strip() | |
| # If still not valid JSON, attempt to extract the first JSON object | |
| if not output.startswith("{"): | |
| match = re.search(r"\{[\s\S]*\}", output) | |
| if match: | |
| output = match.group(0).strip() | |
| # Try to parse the cleaned JSON | |
| data = json.loads(output) | |
| parsed = CatalogResponse(**data) | |
| return parsed.summary.strip() | |
| except (json.JSONDecodeError, ValidationError) as e: | |
| logging.error(f"β JSON parsing/validation failed: {e}") | |
| # Fallback: return the raw output if JSON parsing fails | |
| return output if output else "Unable to generate response." | |