Spaces:
Sleeping
Sleeping
File size: 9,347 Bytes
fbdfc24 3e14b58 fbdfc24 478b91f fbdfc24 |
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# [file name]: core/nodes/response_nodes.py
# Add this as the FIRST lines of code (after docstrings)
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
import logging
import time
from datetime import datetime
from typing import Dict, Any
from langchain_core.runnables import RunnableConfig
from models.state_models import MultiCountryLegalState
from utils.helpers import dict_to_message_obj, message_obj_to_dict
logger = logging.getLogger(__name__)
class ResponseNodes:
def __init__(self, llm):
self.llm = llm
async def response_generation_node(self, state: MultiCountryLegalState, config: RunnableConfig) -> Dict[str, Any]:
"""Generate appropriate responses based on current state"""
assistance_step = state.assistance_step
# Handle assistance workflow responses
if assistance_step == "collecting_email":
response_content = """
Je vois que vous souhaitez parler à un avocat. Pour vous aider, j'ai besoin de votre adresse email pour que notre équipe puisse vous contacter.
📧 **Veuillez me fournir votre adresse email :**
"""
return {
"messages": [{
"role": "assistant",
"content": response_content,
"meta": {"assistance_step": "collecting_email"}
}],
"supplemental_message": "" # Clear any previous supplemental messages
}
elif assistance_step == "collecting_description":
response_content = f"""
Merci ! Votre email ({state.user_email}) a été enregistré.
📝 **Veuillez maintenant décrire brièvement votre situation :**
- Quelle est votre question juridique ?
- De quel pays s'agit-il ?
- Quel type d'assistance recherchez-vous ?
Cette description aidera notre équipe à mieux vous orienter.
"""
return {
"messages": [{
"role": "assistant",
"content": response_content,
"meta": {"assistance_step": "collecting_description"}
}],
"supplemental_message": "" # Clear any previous supplemental messages
}
elif assistance_step == "confirming_send":
response_content = f"""
📋 **RÉCAPITULATIF DE VOTRE DEMANDE :**
📧 **Email :** {state.user_email}
📝 **Description :** {state.assistance_description}
✅ **Confirmez-vous l'envoi de cette demande à notre équipe juridique ?**
Répondez par :
- **"oui"** pour confirmer et envoyer
- **"non"** pour annuler et modifier
"""
return {
"messages": [{
"role": "assistant",
"content": response_content,
"meta": {"assistance_step": "confirming_send"}
}],
"supplemental_message": "" # Clear any previous supplemental messages
}
else:
# Default LLM response for non-assistance flows
return await self._generate_llm_response(state, config)
async def _generate_llm_response(self, state: MultiCountryLegalState, config: RunnableConfig) -> Dict[str, Any]:
"""Generate LLM-based response for normal conversation flows"""
try:
# Include supplemental message in the response if present
supplemental_message = state.supplemental_message or ""
# Synthesize response using LLM
response_content = await self._synthesize_response(state, supplemental_message)
return {
"messages": [{
"role": "assistant",
"content": response_content,
"meta": {
"timestamp": datetime.now().isoformat(),
"generated_by": "llm"
}
}],
"supplemental_message": "" # Clear after using
}
except Exception as e:
logger.error(f"Error generating LLM response: {str(e)}")
return {
"messages": [{
"role": "assistant",
"content": self._create_error_message(str(e)),
"meta": {"is_error": True}
}],
"supplemental_message": f"Erreur: {str(e)}"
}
async def _synthesize_response(self, state: MultiCountryLegalState, supplemental_message: str = "") -> str:
"""Synthesize final response based on graph execution"""
s = state.model_dump()
# Build context-aware system prompt
system_prompt = self._build_system_prompt(state, supplemental_message)
conversation_messages = self._build_conversation_messages(system_prompt, s.get("messages", []))
# Always use LLM to generate final response
logger.info("🧠 Generating final response with LLM")
ai_resp = await self.llm.ainvoke(conversation_messages)
return ai_resp.content if hasattr(ai_resp, 'content') else str(ai_resp)
def _build_system_prompt(self, state: MultiCountryLegalState, supplemental_message: str = "") -> str:
"""Build context-aware system prompt"""
s = state.model_dump()
base_prompt = """Vous êtes un assistant juridique expert spécialisé dans le droit du Bénin et de Madagascar.
TÂCHE: Fournir une réponse claire, précise et utile à l'utilisateur.
"""
# Add supplemental message if available
if supplemental_message:
base_prompt += f"\nMESSAGE IMPORTANT: {supplemental_message}\n"
# Add legal context if available
country_name = s.get("legal_context", {}).get("jurisdiction", "Unknown")
if country_name != "Unknown":
base_prompt += f"\nCONTEXTE JURIDIQUE: Vous répondez dans le cadre du droit {country_name}.\n"
# Add search results if available
search_results = s.get("search_results", "")
if search_results and "RECHERCHE JURIDIQUE" in search_results:
base_prompt += f"\nINFORMATIONS JURIDIQUES DISPONIBLES:\n{search_results}\n"
base_prompt += """
INSTRUCTIONS POUR LA RÉPONSE JURIDIQUE:
- Basez-vous sur les informations juridiques disponibles
- Citez les articles de loi pertinents si possible
- Soyez précis mais accessible aux non-juristes
- Indiquez si certaines informations manquent
"""
else:
base_prompt += """
INSTRUCTIONS GÉNÉRALES:
- Répondez de manière naturelle et utile
- Adaptez votre ton au contexte de la conversation
- Soyez empathique et professionnel
"""
# Add assistance context if relevant
if s.get("assistance_requested"):
base_prompt += "\nCONTEXTE ASSISTANCE: L'utilisateur a demandé à parler à un avocat.\n"
if s.get("approval_status") == "rejected":
base_prompt += "\nCONTEXTE: La demande d'assistance a été rejetée. Expliquez poliment et proposez des alternatives.\n"
elif s.get("approval_status") == "approved":
base_prompt += "\nCONTEXTE: La demande d'assistance a été approuvée. Confirmez et donnez les prochaines étapes.\n"
return base_prompt
def _build_conversation_messages(self, system_prompt: str, messages: list) -> list:
"""Build conversation messages for LLM"""
from langchain_core.messages import SystemMessage
conversation_messages = [SystemMessage(content=system_prompt)]
# Include recent conversation history (last 6 messages)
recent_messages = messages[-6:] if len(messages) > 6 else messages
# Convert to message objects
conversation_messages.extend(dict_to_message_obj(m) for m in recent_messages)
return conversation_messages
async def human_approval_node(self, state: MultiCountryLegalState, config: RunnableConfig) -> Dict[str, Any]:
"""Handle human approval interrupts"""
logger.info("👨⚖️ Human approval node - triggering interrupt")
return {
"approval_status": "pending",
"messages": [{
"role": "assistant",
"content": "⏳ Votre demande d'assistance nécessite une approbation manuelle. Un modérateur va examiner votre demande.",
"meta": {"requires_approval": True}
}],
"supplemental_message": ""
}
async def process_assistance_node(self, state: MultiCountryLegalState, config: RunnableConfig) -> Dict[str, Any]:
"""Process assistance after approval - let LLM generate final message"""
logger.info("📧 Processing assistance request")
return {
"email_status": "sent",
"approval_status": "approved",
"assistance_step": "completed",
"messages": [], # Empty messages so LLM generates the final response
"supplemental_message": "Votre demande d'assistance a été traitée avec succès."
}
def _create_error_message(self, error: str) -> str:
"""Create error message"""
return f"❌ Désolé, une erreur s'est produite: {error}\n\nVeuillez réessayer ou reformuler votre demande." |