""" Este archivo se encarga de generar el texto que el chatbot mostrarĂ¡ al usuario basado en su estado emocional, recomendaciones y eventos anteriores. Se usa la Inference API de HuggingFace como generador principal, con plantilla de respaldo. """ import requests as http_requests from config import GROQ_TOKEN, GROQ_TEXT_MODEL, GROQ_CHAT_URL def construir_respuesta_manual( emocion_dominante: str, modo_recomendacion: str, recomendaciones: list[dict], emocion_previa: dict | None, pelicula_transicion: dict | None, ) -> str: """ Construye la respuesta que el chatbot da en base al estado emocional del usuario Args: - emocion_dominante: El estado emocional actual del usuario (e.g. "alegria", "tristeza"). - modo_recomendacion: El modo de recomendacion basado en la emocion - recomendaciones: Lista de peliculas recomendadas para el usuario. - emocion_previa: El estado emocional anterior del usuario, si existe. - pelicula_transicion: La pelicula vista entre el estado emocional anterior y el actual Returns: - Un texto que el chatbot puede mostrar al usuario explicando su estado emocional, el modo de recomendacion, y mencionando alguna pelicula recomendada. """ # Primero se construye la parte explicativa del estado y modo de recomendacion. if modo_recomendacion == "diferente": mode_text = "Como estas en un estado positivo, te propongo explorar peliculas distintas a tu historial." else: mode_text = "Como estas en un estado negativo, te propongo peliculas similares a tu historial para mantener una zona conocida." # Una vez explicado el modo se menciona la pelicula con mejor puntuacion de las recomendaciones if recomendaciones: top_title = recomendaciones[0].get("title", "una pelicula") reco_text = f"Primera sugerencia: {top_title}." else: reco_text = "No encontre recomendaciones para ese estado emocional en este momento." # Se construye la parte de transicion de una emocion a otra if not emocion_previa: transition_text = "Este es tu primer punto de referencia emocional para analizar transiciones futuras." else: prev_emotion = emocion_previa.get("emotion", "neutral") # Si no se encuentra la emocion previa, se asume neutral para la explicacion. if prev_emotion == emocion_dominante: transition_text = f"Tu estado se mantiene en {emocion_dominante}." # Si la emocion previa es igual a la actual, se explica que no hay transicion. else: transition_text = f"Detecto una transicion de {prev_emotion} a {emocion_dominante}." # Si hay una emocion previa diferente a la actual, se explica la transicion. if pelicula_transicion: movie_title = pelicula_transicion.get("title") or pelicula_transicion.get("movie_id", "pelicula marcada") transition_text += f" Ultima pelicula vista entre ambos estados: {movie_title}." return f"Estado actual: {emocion_dominante}. {mode_text} {reco_text} {transition_text}" def _construir_prompt( emocion_dominante: str, modo_recomendacion: str, recomendaciones: list[dict], emocion_previa: dict | None, pelicula_transicion: dict | None, ) -> str: """ Funcion que construye el prompt para la generacion de texto con Ollama. Se le da contexto sobre el estado emocional actual, el modo de recomendacion, las recomendaciones, la emocion previa y la pelicula de transicion para que genere una respuesta empatica y explicativa. Args: - emocion_dominante: El estado emocional actual del usuario. - modo_recomendacion: El modo de recomendacion basado en la emocion. - recomendaciones: Lista de peliculas recomendadas para el usuario. - emocion_previa: El estado emocional anterior del usuario, si existe. - pelicula_transicion: La pelicula vista entre el estado emocional anterior y el actual. Returns: - Un prompt de texto que se le puede pasar a Ollama para generar una respuesta. """ titulos_peliculas_top = [str(row.get("title", "")).strip() for row in recomendaciones[:3] if row.get("title")] titulos_peliculas_top_text = ", ".join(titulos_peliculas_top) if titulos_peliculas_top else "sin recomendaciones" prev_emotion = emocion_previa.get("emotion") if emocion_previa else "ninguna" titulo_pelicula_transicion = "" if pelicula_transicion: titulo_pelicula_transicion = str(pelicula_transicion.get("title") or pelicula_transicion.get("movie_id") or "").strip() return ( "Eres un asistente de recomendaciones de peliculas. " "Redacta una respuesta breve en espanol (maximo 3 frases), clara y empatica. " "Incluye estado emocional actual, explicacion de por que el modo de recomendacion es ese, " "y menciona una pelicula sugerida si existe. " f"Estado actual: {emocion_dominante}. " f"Modo: {modo_recomendacion}. " f"Emocion anterior: {prev_emotion}. " f"Peliculas sugeridas: {titulos_peliculas_top_text}. " f"Pelicula asociada a transicion: {titulo_pelicula_transicion or 'ninguna'}." ) def generar_texto_chatbot( emocion_dominante: str, modo_recomendacion: str, recomendaciones: list[dict], emocion_previa: dict | None, pelicula_transicion: dict | None, ) -> tuple[str, str]: """ Funcion que genera el texto final a mostrar. Intenta usar Ollama en primera instancia, si falla utiliza la plantilla contruida en construir_respuesta_manual(). Args: - emocion_dominante: El estado emocional actual del usuario. - modo_recomendacion: El modo de recomendacion basado en la emocion. - recomendaciones: Lista de peliculas recomendadas para el usuario. - emocion_previa: El estado emocional anterior del usuario, si existe. - pelicula_transicion: La pelicula vista entre el estado emocional anterior y el actual. Returns: - Un tuple con el texto generado y la fuente del texto ("ollama" o "template-fallback"). """ respuesta_manual = construir_respuesta_manual( emocion_dominante=emocion_dominante, modo_recomendacion=modo_recomendacion, recomendaciones=recomendaciones, emocion_previa=emocion_previa, pelicula_transicion=pelicula_transicion, ) try: prompt = _construir_prompt( emocion_dominante=emocion_dominante, modo_recomendacion=modo_recomendacion, recomendaciones=recomendaciones, emocion_previa=emocion_previa, pelicula_transicion=pelicula_transicion, ) url = GROQ_CHAT_URL headers = { "Authorization": f"Bearer {GROQ_TOKEN}", "Content-Type": "application/json", } payload = { "model": GROQ_TEXT_MODEL, "messages": [{"role": "user", "content": prompt}], "max_tokens": 120, "temperature": 0.7, "top_p": 0.9, } res = http_requests.post(url, headers=headers, json=payload, timeout=20) res.raise_for_status() data = res.json() generated = str(data["choices"][0]["message"]["content"]).strip() if data.get("choices") else "" if generated: return generated, "groq" print("Aviso: Groq devolvio respuesta vacia. Se usa plantilla.") except Exception as exc: print(f"Aviso: fallo generando texto con Groq ({exc}). Se usa plantilla.") return respuesta_manual, "template-fallback"