File size: 7,610 Bytes
3998131 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
"""
Letter Generator Module
Combines templates with user data to produce final letters.
"""
import logging
from typing import Dict, Any, Optional
from .template_loader import TemplateLoader
from .llm_client import MistralClient
logger = logging.getLogger(__name__)
class LetterGenerator:
"""
Handles the generation of letters from templates and user data.
"""
def __init__(self):
self.loader = TemplateLoader()
try:
self.llm = MistralClient()
except Exception as e:
logger.warning(f"LLM Client could not be initialized: {e}. LLM features will be disabled.")
self.llm = None
def generate_letter(self, template_name: str, user_data: Dict[str, str]) -> str:
"""
Generate a letter by filling in a template with user data.
Performs simple substitution.
"""
template_text = self.loader.load_template(template_name)
# 1. Simple Substitution
# We iterate through keys in user_data and replace corresponding placeholders
# We try to match [Key], {{Key}}, <Key>
generated_text = template_text
for key, value in user_data.items():
# Replace [Key]
generated_text = generated_text.replace(f"[{key}]", str(value))
# Replace {{Key}}
generated_text = generated_text.replace(f"{{{{{key}}}}}", str(value))
# Replace <Key>
generated_text = generated_text.replace(f"<{key}>", str(value))
# Replace {Key}
generated_text = generated_text.replace(f"{{{key}}}", str(value))
return generated_text
def refine_with_llm(self, draft_letter: str, instructions: str = "") -> str:
"""
Use LLM to polish or refine the letter.
"""
if not self.llm:
logger.warning("LLM not available for refinement.")
return draft_letter
prompt = f"""
You are a helpful legal assistant for Nepal.
Please refine the following letter to be more professional and grammatically correct.
Ensure it remains factual to the original content.
Do not add any fake information.
Instructions: {instructions}
Draft Letter:
{draft_letter}
Refined Letter:
"""
return self.llm.generate_response(prompt)
def analyze_requirements(self, description: str) -> Dict[str, Any]:
"""
Analyzes the user description against the best matching template
to identify missing information.
"""
if not self.llm:
raise RuntimeError("LLM required for analysis.")
from .retriever import TemplateRetriever
retriever = TemplateRetriever()
retrieved_templates = retriever.retrieve_templates(description, k=1)
if not retrieved_templates:
return {"success": False, "error": "No relevant template found."}
best_template = retrieved_templates[0]
template_content = best_template['content']
template_name = best_template['filename']
# Extract placeholders from the template
placeholders = self.loader.extract_placeholders(template_content)
if not placeholders:
return {
"success": True,
"template_name": template_name,
"detected_placeholders": [],
"missing_fields": []
}
# Ask LLM which fields are missing from the description
prompt = f"""
You are an intelligent assistant.
I have a letter template with the following required placeholders: {list(placeholders)}
The user provided this description: "{description}"
Identify which placeholders are MISSING or cannot be inferred from the description.
Return ONLY a comma-separated list of missing placeholders. If none are missing, return "None".
Missing Placeholders:
"""
response = self.llm.generate_response(prompt, temperature=0.0)
missing_fields = []
if "None" not in response:
# Clean up response
cleaned = response.replace("\n", "").strip()
if cleaned:
missing_fields = [f.strip() for f in cleaned.split(",") if f.strip()]
return {
"success": True,
"template_name": template_name,
"detected_placeholders": list(placeholders),
"missing_fields": missing_fields
}
def generate_from_description(self, description: str, additional_data: Dict[str, str] = None, template_name: str = None) -> Dict[str, Any]:
"""
RAG-Based Generation:
1. Retrieve relevant template based on description (OR use provided template_name).
2. Use LLM to fill/adapt the retrieved template, incorporating additional data.
"""
if not self.llm:
raise RuntimeError("LLM required for smart generation.")
best_template = None
retrieval_score = 1.0 # Default if manual selection
if template_name:
# Direct template usage
try:
content = self.loader.load_template(template_name)
best_template = {
"filename": template_name,
"content": content,
"score": 1.0
}
logger.info(f"Using specified template: {template_name}")
except Exception as e:
return {"success": False, "error": f"Template '{template_name}' not found: {e}"}
else:
# RAG Retrieval
from .retriever import TemplateRetriever
retriever = TemplateRetriever()
retrieved_templates = retriever.retrieve_templates(description, k=1)
if not retrieved_templates:
return {
"success": False,
"error": "No relevant template found."
}
best_template = retrieved_templates[0]
retrieval_score = best_template['score']
template_content = best_template['content']
template_name = best_template['filename']
logger.info(f"Selected template: {template_name}")
# Format additional data for the prompt
additional_info_str = ""
if additional_data:
additional_info_str = "\nAdditional User Details:\n" + "\n".join(f"- {k}: {v}" for k, v in additional_data.items())
# Prompt LLM to fill the retrieved template
prompt = f"""
You are a helpful legal assistant for Nepal.
Your task is to write a formal letter based on the user's description, using the provided template as a strict guide.
User Description: "{description}"
{additional_info_str}
Selected Template ({template_name}):
{template_content}
Instructions:
1. Use the structure and formal language of the Selected Template.
2. Fill in the placeholders (like [Name], {{Date}}) with information from the User Description and Additional Details.
3. If information is still missing, use a generic placeholder like "[Insert Name]".
4. Output ONLY the final letter in Nepali (or English if the template is English). Do not add conversational text.
Final Letter:
"""
generated_letter = self.llm.generate_response(prompt, temperature=0.3)
return {
"success": True,
"letter": generated_letter,
"template_used": template_name,
"retrieval_score": retrieval_score
}
|